We caught an architecture mistake in our own spec before it shipped.
The early draft of the Cadencz backend spec assumed Next.js could carry both the frontend and a chunk of the product API, using its /app/api routes as a shortcut. On paper it saves a service. In practice it quietly merges two things that should stay apart.
The correction we made: Next.js is frontend only. No product API lives inside it. A separate backend owns every OAuth callback, every billing webhook, every scheduling state change, every AI credit check.
This isn't a framework preference. It's about what each of those things actually needs.
OAuth callbacks need a stable, versioned endpoint that doesn't move when someone redesigns a page. Billing webhooks from a payment provider need a service that can retry, log, and stay up even if the frontend deploy is mid-rollout. Scheduling state changes need a source of truth that isn't tied to a request/response cycle built for rendering HTML.
Mixing these into the same codebase as your UI works fine until you need to redeploy the frontend without touching billing logic, or debug a failed webhook without wading through page components. Then the coupling shows up as an incident, not a code smell.
The tradeoff is real. Two services mean two deploy pipelines, two places to monitor, and more upfront setup than a monolith. For a weekend project, that overhead isn't worth it.
For anything that touches money or third-party auth, it is. A billing webhook failing silently because your frontend build broke is a worse outcome than the extra infrastructure it takes to prevent it.
The lesson generalizes past this one stack. Frontend and backend separation is a decision you make based on what your system touches, not a default you inherit from a tutorial. If your app never handles payments or OAuth, a combined setup might be the right call. If it does, treat the split as a requirement, not a nice-to-have.
What would you add? Where has skipping this separation actually bitten you in production?
Top comments (0)