Build the mental model
This project takes a single app and forces you to design it twice, which is the fastest way to feel the BaaS comparison lesson's tradeoffs instead of just reading about them. The app is NoteSync: a notes app with user accounts, notes storage, and realtime sync across a person's devices.
| Axis | Tradeoff |
|---|---|
| Development speed | BaaS ships fastest; custom backend takes longer to reach a working first version. |
| Control | Custom backend gives full control over logic and data; BaaS constrains you to its SDK and rules. |
| Cost structure | BaaS is usage-based and cheap at first but can rise quickly; custom backend cost is more predictable but exists even at low usage. |
| Portability | Custom backend code and database move between PaaS providers more easily than BaaS-tied schemas and hooks. |
| Complexity growth | BaaS gets awkward as business logic outgrows CRUD and rules; custom backend absorbs growth naturally. |
Both designs are legitimate. That is the point this project exists to make, and it is easy to skip past if you go looking for a single winner. The BaaS version ships fastest but trades away control; the custom-backend version costs more time up front but stays portable and gives full control.
No universal winner
The right choice depends on priorities like time-to-market and expected complexity growth, not on which architecture sounds more sophisticated.
NOTESYNC: BAAS VS CUSTOM BACKEND
--------------------------------
BAAS VERSION CUSTOM BACKEND VERSION
Frontend Frontend
| |
v v
BaaS (Auth+DB+Realtime) Custom Backend Service
|
v
Database
(+ its own Auth, Realtime)Connect it to a real scenario
Sketch the BaaS version
Draw frontend -> BaaS (auth + database + realtime built in) with almost no custom backend code of your own.
Sketch the custom-backend version
Draw frontend -> custom backend -> database, with auth and realtime as the backend's own responsibility.
Fill in the tradeoff table
Before looking at the code, fill in five rows by hand: development speed, control, cost, portability, complexity growth.
Run it and check leansTowards
Run compareArchitectures, compare its result to your own guesses, and confirm the recommendation shifts when priorities change.
Try the working example
function compareArchitectures(app) {
const baas = {
approach: "BaaS (Supabase/Firebase-style)",
devSpeed: "Fast: auth, database, and realtime sync are provided out of the box, so there is far less custom code to write and test.",
control: "Lower: business logic is constrained to what the BaaS SDK, database rules, and realtime hooks allow.",
costStructure: "Usage-based pricing from day one; near-zero cost while small, but it can rise quickly at scale.",
portability: "Lower: the schema, auth, and realtime wiring are tied to that provider's SDK and conventions.",
complexityGrowth: "Bolting on custom business logic beyond simple CRUD and rules gets progressively harder."
};
const customBackend = {
approach: "Custom backend on a PaaS",
devSpeed: "Slower start: auth, schema, and realtime sync all have to be designed, built, and tested by hand.",
control: "Higher: full control over business logic, data model, and the realtime implementation.",
costStructure: "Predictable compute and database cost, but you pay for infrastructure even at low usage.",
portability: "Higher: it's a standard backend codebase and database that can move between PaaS providers.",
complexityGrowth: "Scales cleanly as business logic grows, since it is already an ordinary application codebase."
};
let leansTowards;
if (app.timeToMarketPriority === "high" && app.expectedComplexityGrowth !== "high") {
leansTowards = "BaaS - speed to ship matters more here, and the logic isn't expected to outgrow it soon.";
} else if (app.expectedComplexityGrowth === "high" || app.teamSize === "large") {
leansTowards = "Custom backend - complex logic or a larger team benefits more from full control than from speed.";
} else {
leansTowards = "Either is defensible - revisit this once real usage patterns and team size are clearer.";
}
return { app: app.name, baas, customBackend, leansTowards };
}
const noteApp = {
name: "NoteSync (notes app with accounts + realtime sync across devices)",
timeToMarketPriority: "high",
expectedComplexityGrowth: "low",
teamSize: "solo"
};
console.log(JSON.stringify(compareArchitectures(noteApp), null, 2));Running compareArchitectures for NoteSync prints an object with baas and customBackend sub-objects (each listing devSpeed, control, costStructure, portability, and complexityGrowth) plus a leansTowards field reading 'BaaS - speed to ship matters more here, and the logic isn't expected to outgrow it soon.'5-minute try-it
Change NoteSync's teamSize to 'large' or expectedComplexityGrowth to 'high' and re-run compareArchitectures. Explain in one sentence why leansTowards flips to custom backend even though the two architecture descriptions themselves haven't changed at all.
One important caution
Assuming BaaS is always the 'beginner' choice and custom backend is always 'more professional' -- the right pick depends on priorities, not sophistication.
Comparing the two architectures only on initial build cost while ignoring how each handles the app's business logic growing more complex later.
Supabase vs Firebase — Cloud Providers & Platforms