Introduction

Coupling a UI and an API in one deploy path is convenient until one of them needs to move faster than the other. A directory product made that tension visible: the interface had to stay usable while the data layer and endpoints kept changing.

This article covers the split I used: a dedicated frontend repository and a dedicated backend service, each deployed independently, with a contract between them instead of a shared codebase.

The Problem

When the interface and the data layer ship together, a copy change and a schema change become the same release. That slows both sides. It also makes failures harder to isolate: a blank page could be a UI bug or a backend timeout.

The constraint was a public product that still needed to evolve. A monolith would have been simpler to start. It would not have stayed simple once the backend needed its own environment, validation, and clients.

Approach

The frontend owns presentation, routing, and client-side fetching. The backend owns data, validation, and persistence. They meet at HTTP, not at shared folders.

Each side has its own repository and its own production URL. That makes the contract explicit: if the API shape changes, the frontend has to change against a versioned endpoint, not against an internal import.

Implementation

The frontend lives in `BeTainos-directory-frontend` and deploys as its own Vercel project. The backend lives in `dcl_api` and deploys separately. The UI does not bundle server secrets; it calls the public service.

That split also made local work clearer. The interface can be developed against a deployed backend. The service can be tested without running the full UI. The cost is contract discipline: breaking response shapes become visible immediately.

Challenges & Trade-offs

Two repositories mean two deploy pipelines and two places to look when something fails. That is real overhead. I accepted it because the alternative was a single release that mixed unrelated risk.

A BFF in the frontend would have hidden the backend, but it would also have pulled server concerns back into the UI project. For this product, a public API was the clearer boundary.

Impact

Results / Impact

The split produced a chain of evidence that is easy to inspect: a live interface, a live backend, and two repositories that show how the work is actually organized.

2 repos

Frontend and backend versioned independently

Live UI

Public frontend on Vercel

Live API

Public backend on its own deploy

  • UI and backend can ship on different schedules.
  • Production failures are easier to attribute to the interface or the data layer.
  • The HTTP contract is the documentation, not a shared internal module.

What I Learned

The contract is the product. If response shapes are sloppy, the split creates more work than it saves.

I would define a small public schema earlier, even before the UI is pretty. The frontend should consume a stable surface, not whatever the backend happened to return that week.

Future Improvements

Typed client generation and explicit API versioning would make the boundary safer as more consumers appear. The current split is enough for one interface. It is not yet a platform.