DEV Community

Nana Okamoto
Nana Okamoto

Posted on

Why BFF Architecture Makes More Sense in the Age of AI-Assisted Development

AI coding tools are getting better at generating code.

But as AI writes more of our application code, a different problem becomes increasingly important:

How much of the system does the AI need to understand before it can safely make a change?

This is where architecture starts to matter.

A Backend for Frontend (BFF) is usually introduced as a way to create APIs optimized for a specific frontend. But in AI-assisted development, it offers another interesting advantage:

A BFF creates a clear boundary where frontend requirements can be translated into explicit, testable API contracts.

That boundary can reduce the amount of context an AI coding agent needs, make generated changes easier to review, and limit the blast radius of mistakes.

In this article, I’ll explain why BFF architecture can become especially useful when AI is part of the development workflow.


What Is a Backend for Frontend?

A Backend for Frontend is a backend layer designed specifically for the needs of a particular user interface.

Instead of this:

Frontend
   ↓
Multiple backend services
   ↓
Databases / external APIs
Enter fullscreen mode Exit fullscreen mode

you introduce a frontend-oriented layer:

Frontend
   ↓
BFF
   ↓
Backend services
   ↓
Databases / external APIs
Enter fullscreen mode Exit fullscreen mode

The BFF can handle responsibilities such as:

  • aggregating multiple APIs
  • transforming backend data into frontend-friendly responses
  • authentication and authorization integration
  • pagination
  • error normalization
  • hiding internal service structure
  • defining frontend-specific API contracts

The key idea is not simply "add another API."

The important part is ownership and boundaries.

The frontend communicates with an API designed around its own use cases rather than depending directly on the structure of internal systems.


Why Does This Matter for AI-Assisted Development?

AI coding agents work best when the task has clear boundaries.

For example, compare these two instructions.

Without a clear architecture

Add the user's subscription status to the dashboard.

The AI may need to discover:

  • where subscription data lives
  • which service owns it
  • how authentication works
  • whether the frontend can access the service directly
  • how responses are transformed
  • which fields are safe to expose
  • what happens when the service is unavailable
  • whether multiple frontend components already implement similar logic

The implementation space becomes very large.

Now consider the same feature with a BFF.

Dashboard
   ↓
GET /api/dashboard
   ↓
Dashboard BFF
   ├── User Service
   └── Subscription Service
Enter fullscreen mode Exit fullscreen mode

The task becomes much more constrained:

Add subscriptionStatus to the dashboard response.

Now the AI can focus on a much smaller path:

Subscription Service
        ↓
       BFF
        ↓
DashboardResponse
        ↓
Frontend
Enter fullscreen mode Exit fullscreen mode

This is a much better environment for both humans and AI.


1. BFF Reduces the Context AI Needs to Understand

One of the hidden costs of AI-generated code is context size.

The larger the architectural surface an AI needs to understand, the more opportunities there are for incorrect assumptions.

Consider a frontend that directly consumes several services:

React / Next.js
├── User API
├── Billing API
├── CMS API
├── Search API
└── Recommendation API
Enter fullscreen mode Exit fullscreen mode

A developer changing one page may need to understand all five integrations.

An AI agent faces the same problem.

With a BFF:

React / Next.js
        ↓
       BFF
        ↓
 ┌──────┼──────┐
User  Billing  CMS
Enter fullscreen mode Exit fullscreen mode

the frontend only needs to understand one interface.

This creates what I think of as an AI-friendly context boundary.

The AI working on the frontend does not necessarily need to understand every downstream system.

It needs to understand the BFF contract.

That is a much smaller problem.


2. API Contracts Give AI an Explicit Source of Truth

AI-generated implementations become significantly safer when APIs are defined through machine-readable contracts.

For example:

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  subscriptionStatus: z.enum([
    "free",
    "pro",
    "enterprise",
  ]),
})
Enter fullscreen mode Exit fullscreen mode

Or an OpenAPI schema:

User:
  type: object
  required:
    - id
    - name
    - subscriptionStatus
  properties:
    id:
      type: string
    name:
      type: string
    subscriptionStatus:
      type: string
      enum:
        - free
        - pro
        - enterprise
Enter fullscreen mode Exit fullscreen mode

Now both developers and AI agents have an explicit definition of:

  • What data exists
  • What type it has
  • What values are valid
  • What the frontend can depend on

Without a contract, AI often has to infer these things from implementation details.

Inference is exactly where mistakes begin.


3. BFF Prevents Backend Structure From Leaking Into the UI

Suppose the frontend needs this:

type Dashboard = {
  userName: string
  plan: string
  unreadNotifications: number
}
Enter fullscreen mode Exit fullscreen mode

But the data comes from three different systems:

  • Identity Service
  • Billing Service
  • Notification Service

Without a BFF, the frontend may end up knowing all of this.

const user = await getUser()
const billing = await getSubscription()
const notifications = await getNotifications()

return {
  userName: user.profile.display_name,
  plan: billing.subscription.product.plan_name,
  unreadNotifications: notifications.metadata.unread_count,
}
Enter fullscreen mode Exit fullscreen mode

This creates coupling between the UI and backend implementation details.

A BFF can instead expose:

GET /api/dashboard
Enter fullscreen mode Exit fullscreen mode
{
  "userName": "Nana",
  "plan": "pro",
  "unreadNotifications": 3
}
Enter fullscreen mode Exit fullscreen mode

The frontend now depends on a product-level contract, not the internal structure of three services.

This distinction becomes even more useful when AI is modifying the codebase.

An AI agent can change the dashboard without needing to reason about the entire service topology.


4. BFF Gives AI a Safer Place for Data Aggregation

Modern interfaces frequently need data from several sources.

For example:

Product Page
├── Product Service
├── Inventory Service
├── Review Service
└── Recommendation Service
Enter fullscreen mode Exit fullscreen mode

You could perform this orchestration inside the frontend.

But then UI code starts accumulating infrastructure concerns.

const [
  product,
  inventory,
  reviews,
  recommendations
] = await Promise.all([
  getProduct(id),
  getInventory(id),
  getReviews(id),
  getRecommendations(id),
])
Enter fullscreen mode Exit fullscreen mode

Soon you also need:

  • retry policies
  • timeout handling
  • authentication
  • fallback behavior
  • response transformations
  • partial failure handling

That logic becomes difficult for both developers and AI to reason about.

A BFF provides a natural place for this orchestration.

app.get("/products/:id", async (c) => {
  const id = c.req.param("id")

  const [product, inventory, reviews] =
    await Promise.all([
      productService.get(id),
      inventoryService.get(id),
      reviewService.get(id),
    ])

  return c.json({
    id: product.id,
    name: product.name,
    available: inventory.quantity > 0,
    rating: reviews.average,
  })
})
Enter fullscreen mode Exit fullscreen mode

The frontend receives exactly what it needs.

const product = await api.products.get(id)
Enter fullscreen mode Exit fullscreen mode

That smaller interface is easier to generate, test, review, and maintain.


5. BFF Can Reduce the Blast Radius of AI-Generated Changes

AI makes development faster.

It can also make developers comfortable changing more code at once.

That makes architectural boundaries increasingly valuable.

Imagine an AI agent needs to modify how customer data is displayed.

Without a clear boundary:

Frontend
   ↓
Customer Service
   ↓
Shared database models
Enter fullscreen mode Exit fullscreen mode

A generated change might accidentally depend on internal fields or introduce coupling with another service.

With a BFF:

Frontend
   ↓
CustomerResponse
   ↓
BFF
   ↓
Customer Service
Enter fullscreen mode Exit fullscreen mode

the contract acts as a boundary.

Changes can be evaluated at each step.

Did the downstream API change?
        ↓
Does the BFF still satisfy its contract?
        ↓
Does the frontend still satisfy its tests?
Enter fullscreen mode Exit fullscreen mode

This makes automated verification much easier.


6. BFF Works Well With Contract Tests and Generated Tests

One of the best ways to use AI safely is not simply:

Generate more code.

It is:

Generate code inside a system that can automatically tell us when the code is wrong.

BFF architecture makes this easier because the interface between the frontend and backend is explicit.

For example:

OpenAPI / Zod Schema
        ↓
      BFF
        ↓
Contract Tests
        ↓
Frontend Types
        ↓
Integration Tests
Enter fullscreen mode Exit fullscreen mode

An AI agent can modify an endpoint.

Then automated checks verify:

✓ response matches schema
✓ unauthorized requests fail
✓ required fields exist
✓ frontend types still compile
✓ integration tests still pass

The architecture itself provides guardrails.

That is far more scalable than expecting developers to manually inspect every line of AI-generated code.


7. BFF Creates Better Tasks for Coding Agents

Consider giving an AI coding agent this task:

Add a recommended schools section to the homepage.

If the system has no clear boundaries, the AI first needs to explore the repository and determine:

  • Where does school data come from?
  • Where are recommendations calculated?
  • Can the frontend access the database?
  • Does authentication matter?
  • What fields should be exposed?
  • Is there already an API?
  • Where should caching happen?

A well-structured system allows the task to be expressed much more precisely:

Implement:
GET /api/home/recommended-schools

Response:
{
  schools: SchoolSummary[]
}

Requirements:
- maximum 6 schools
- only active schools
- sort by recommendation score
- cache for 10 minutes
Enter fullscreen mode Exit fullscreen mode

This is exactly the type of task AI agents handle well.

Architecture turns vague requirements into bounded implementation problems.


8. Frontend and AI Can Share the Same Contract

Another advantage appears when TypeScript is used across the stack.

For example:

apps/
  web/

services/
  bff/

packages/
  api-contract/
Enter fullscreen mode Exit fullscreen mode

The shared contract might contain:

export const SchoolSummarySchema = z.object({
  id: z.string(),
  name: z.string(),
  city: z.string(),
  thumbnailUrl: z.string().url(),
})

export type SchoolSummary =
  z.infer<typeof SchoolSummarySchema>
Enter fullscreen mode Exit fullscreen mode

Now:

Backend implementation
        ↓
Shared contract
        ↑
Frontend implementation
Enter fullscreen mode Exit fullscreen mode

Both human developers and AI agents can inspect the same source of truth.

This eliminates an entire category of ambiguity.

Instead of asking:

What does this endpoint return?

the answer exists in code.


BFF Is Not Automatically "AI Architecture"

There is an important caveat.

Adding a BFF does not automatically make a system AI-friendly.

A bad BFF can easily become another monolith.

For example:

BFF
├── authentication
├── billing rules
├── recommendation engine
├── analytics logic
├── email sending
├── database access
├── admin workflows
├── batch processing
└── everything else
Enter fullscreen mode Exit fullscreen mode

At that point, you have simply created another backend.

A BFF should primarily translate between:

What the UI needs
        ↕
What backend systems provide
Enter fullscreen mode Exit fullscreen mode

It should not become the default location for every piece of business logic.


When BFF Might Be Overkill

A BFF is not necessary for every application.

Consider skipping it when:

  • your application has a single simple API
  • the frontend already receives exactly the data it needs
  • there is little server-side aggregation
  • introducing another service adds more operational complexity than value
  • your existing GraphQL architecture already provides an effective frontend-specific abstraction

The goal is not:

Every AI-powered development team needs a BFF.

The better principle is:

AI benefits from explicit architectural boundaries, and BFF is one practical way to create one between the UI and backend systems.


What an AI-Friendly BFF Looks Like

An AI-friendly BFF does not require a special architecture.

The important part is keeping the boundary predictable.

In practice, that usually means:

  • the frontend talks to a small, well-defined API surface
  • request and response schemas are explicit
  • the BFF handles aggregation and frontend-specific transformation
  • core business logic stays in the appropriate backend service
  • contracts are validated through types, schemas, and tests
  • changes can be verified automatically before human review

For example, in a TypeScript stack, OpenAPI or Zod can act as the contract between the frontend and the BFF.

That creates a simple development loop:

Requirement → API contract → implementation → automated verification → review

This workflow is useful for human developers too, but it becomes especially valuable when coding agents are generating larger portions of the implementation.

The goal is not to build an architecture for AI.

The goal is to make the existing architecture explicit enough that both humans and AI can modify it safely.


The Bigger Idea: Optimize Architecture for Change

For years, architecture discussions have focused on things like:

  • scalability
  • performance
  • reliability
  • team ownership
  • deployment independence

AI adds another dimension:

How easily can an agent understand and safely modify this system?

That does not mean designing applications specifically for LLMs.

It means that characteristics we already considered good engineering practices become even more valuable:

  • Clear boundaries
  • Explicit contracts
  • Small interfaces
  • Automated verification
  • Predictable ownership
  • Limited blast radius

A BFF happens to reinforce many of these characteristics.


Final Thoughts

The biggest advantage of a BFF in AI-assisted development is not API aggregation.

It is context control.

A good BFF gives both developers and AI agents a clear answer to three important questions:

  • What does the frontend need?
  • What contract guarantees it?
  • Where should the transformation happen?

When those answers are explicit, AI-generated code becomes easier to produce, easier to verify, and easier to review.

As coding agents become responsible for larger changes, I think this type of architectural clarity will matter more — not less.

AI can generate code quickly. Good architecture decides how much of the system it needs to understand before doing so.

And that may become one of the most important reasons to care about architectural boundaries in the AI era.


FAQ

Is BFF architecture useful for AI coding agents?

Yes, particularly in applications with multiple backend services or external data sources. A BFF can give coding agents a smaller, explicit API surface instead of requiring them to understand every downstream system.

Does BFF make AI-generated code safer?

Not by itself. However, combining a BFF with typed API contracts, schema validation, contract tests, and automated integration tests can significantly reduce ambiguity and make generated changes easier to verify.

Should every Next.js application use a BFF?

No. For simple applications, introducing another layer may create unnecessary complexity. BFF becomes more valuable when the frontend needs data aggregation, frontend-specific transformations, independent API evolution, or isolation from multiple downstream systems.

What makes a BFF AI-friendly?

The most important characteristics are:

  • small and explicit API contracts
  • schema validation
  • predictable ownership
  • limited responsibilities
  • generated or shared types
  • automated tests
  • clear separation from core business logic

Top comments (0)