In the AI Era, Frontend-Backend Separation Should No Longer Be the Default

A reflection on why AI changes the cost-benefit balance of frontend-backend separation, and why product-layer full stack should become the default for many projects.

I used to think about frontend-backend separation this way: the frontend and backend could be separated, but the people building them should not be separated.

In other words, system boundaries can exist. Interfaces can be clear. Frontend and backend code can have different structures. But the person responsible for a feature should understand the full path from page to data, from interaction to business rule. Otherwise, frontend engineers only call APIs, backend engineers only return JSON, and everyone ends up responsible for one slice of the chain while nobody is truly responsible for the user experience or the business result.

My view has moved further now. In the AI era, many projects should no longer treat frontend-backend separation itself as the default. More precisely, the product layer should not default to frontend-backend separation.

Chinese version of this article

What Product-Layer Full Stack Means

Product-layer full stack does not mean putting every system into one giant monolith. It also does not deny the value of platform capabilities, core systems, or data infrastructure.

It means that a user-facing product feature should, as much as possible, be completed in one context: interface, interaction, data access, permissions, state, submission, validation, business rules, persistence, and deployment.

For small and medium-sized projects, this is often the whole business. For larger systems, it can still be the complete product loop around one business domain. Capabilities such as payment, login, search, recommendation, risk control, analytics, and data warehouses can become platform capabilities or business dependencies when they are complex enough to evolve independently.

Put differently, how users place orders, how courses are sold, how articles are published, or how tasks move through a workflow are still product business logic. They should usually stay inside the product layer’s complete context. Lower-level capabilities can be split out, but a product feature should not naturally be cut into two half-finished pieces called “frontend” and “backend”.

Separation Used to Make Sense

Frontend-backend separation used to solve a people problem.

Frontend and backend stacks were different. Their concerns were different. As teams grew, they needed collaboration boundaries. Interface contracts reduced coordination cost. Independent deployment could reduce mutual impact. When tools were weaker and cross-stack work was expensive for individuals, those arguments made sense.

But many teams later turned this into a default sign of engineering maturity. As soon as a Web app was built, there would be a frontend project and a backend project. As soon as a page needed data, a REST or GraphQL API would be designed. As soon as there were frontend and backend roles, two groups of people would own the two halves by default.

Over time, “frontend engineers should not touch the backend” and “backend engineers should not write pages” became an almost instinctive organizational assumption.

That assumption is becoming more questionable in the AI era.

AI Needs Complete Context

AI does not only change a framework detail. It changes a developer’s ability to handle context. The quality of AI-generated code depends heavily on context completeness.

If a feature’s page, data structure, permission logic, submission flow, error handling, cache behavior, and tests all live in one project, one type system, and related file structures, AI can understand the causal relationships more easily and make more coherent changes.

The opposite is also true. If the frontend lives in one repository and the backend in another; if the frontend only sees an API document while the backend cannot see how the page actually uses the data; if deployment is separated; if types have to be synchronized through generated code or documents, then both humans and AI have to reconstruct context from fragments.

That cost used to be paid by humans. Now it also directly affects the quality of AI output.

This is why I have started to understand isomorphic full-stack frameworks differently.

Isomorphic Frameworks Are Not Only About SSR

The value of frameworks such as Next.js is not only that SSR can improve first load experience. It is also not only that API routes and pages can live in the same project. The more important point is that they merge the context of a product feature back together.

How the page is rendered, how data is loaded, how actions are submitted, where permissions are checked, how types flow, and when caches are invalidated can be handled inside one engineering model. For developers, this reduces cognitive overhead. For AI, it improves context quality.

I also like the direction represented by Vinext: preserving the full-stack isomorphic development experience associated with Next.js while exploring a more open build and runtime path through ecosystems such as Vite and Cloudflare Workers. Vinext is still experimental, and the framework itself is not the point. The more important trend is that development context is being merged again, and full-stack frameworks will increasingly evolve around AI-friendly engineering models.

Traditional full-stack frameworks such as Django, Rails, and Laravel are also part of the full-stack path. They have long proved that completing product features in one project is not an outdated idea. The difference is that for modern Web applications with heavier frontend interaction and stronger dependence on component ecosystems, frameworks such as Next.js and Vinext are closer to how current frontend engineering works.

Contracts Do Not Disappear

One common argument for frontend-backend separation is interface contracts.

Contracts are still important, but they do not have to take the form of an internal HTTP API pretending to be a public service. External systems, mobile clients, multi-client reuse, and third-party integrations certainly need stable APIs. But data interfaces consumed only by a Web UI do not naturally need to become public contracts.

After moving to product-layer full stack, contracts do not disappear. They become internalized. They can be TypeScript types, schemas, server actions, component props, database models, unit tests, integration tests, and end-to-end tests. Compared with an API document maintained across separate frontend and backend repositories, these contracts are closer to the code that actually runs and easier for AI and tools to understand together.

Databases follow a similar logic. In a small project, it is normal for the product layer to read and write the database directly. In a large project, data can be accessed through domain services, storage services, or platform capabilities. But a page-only API should not be created merely to satisfy the doctrine of frontend-backend separation.

The Role of BFF Also Changes

BFF has a similar issue.

Many BFF implementations are glue created after frontend-backend separation: forwarding APIs, trimming fields, assembling data, adding some authentication, and converting formats. Their existence proves the opposite of what pure API thinking assumes: a generic API does not always serve page experience well.

If that is the case, many low-value BFF layers should be absorbed back into product-layer full stack.

BFF is not always worthless. If it handles complex aggregation, security governance, traffic control, caching, canary rollout, fallback behavior, or shared experience orchestration across multiple products, it can justify being an independent system. But if it exists only so that the frontend never has to touch the backend, it may simply be extra complexity produced by an organizational boundary.

Teams Should Be Organized by Business Domain

A better organization model is also not to split people by frontend and backend. It is to organize small full-stack teams by business domain.

Engineers inside one business domain can still have specialties. Some people may be better at interaction, some at data, some at infrastructure. But the team as a whole should own the complete loop from product requirements to production operation.

When people are split by technical stack for too long, each engineer understands only one segment of the chain. Eventually, nobody fully understands how user needs become working product behavior.

This does not mean every project should avoid separation. Multi-client reuse of the same API, open platforms, complex core business systems, strong security or compliance requirements, extreme performance constraints, very large-team collaboration, and top-tier consumer applications at major companies can all justify clear frontend-backend boundaries. Multi-client scenarios are especially important: when the same business capability has to serve Web, iOS, Android, Mini Programs, and third-party systems, stable APIs become much more valuable.

But those should be reasons for separation, not the starting point.

A Small Personal Observation

My own experience is straightforward. In personal projects, I used to use NestJS plus a Web page. That combination works, but interface definitions, type synchronization, integration work, deployment, file hopping, and context switching keep appearing.

After moving to full-stack isomorphic solutions such as Vinext, the most obvious change was not writing a few fewer lines of code. It was that one feature could finally be completed in one context. That matters for humans, and it matters even more for AI.

This is not a rigorous conclusion from a massive production system. It is closer to an architectural preference accumulated while actually writing code. But many architecture decisions eventually return to a plain question: does this structure help people understand the business faster, or does it create more boundaries that have to be synchronized?

Default to Full Stack Unless There Is a Reason to Split

So my current default judgment is:

  • If there is only one primary Web client, prefer product-layer full stack.
  • If page data strongly depends on UI shape, prefer product-layer full stack.
  • If an API mainly serves its own pages rather than outside consumers, prefer product-layer full stack.
  • If the team is not large enough to require strong boundaries, prefer product-layer full stack.
  • If the business becomes complex, split by business domain and platform capability before splitting by frontend and backend.

When discussing architecture, the first question should no longer be:

Should we separate frontend and backend?

It should be:

Does this business have a strong enough reason to split the product context apart?

If not, frontend-backend separation should no longer be the default. It is not a symbol of advanced engineering. It is a form of complexity that needs to justify itself.