SQL vs NoSQL for App Backends: How to Actually Choose
Few architecture debates generate more heat and less light than SQL versus NoSQL. Strip out the tribalism and the decision is straightforward — because the two aren't competing products; they're different trade-offs, and most apps clearly sit on one side.
What a relational database gives you by default
Start with what you get from Postgres or MySQL without doing anything special, because it's easy to forget how much this is:
- A schema. The database enforces that data has the shape your code expects. Bad writes fail loudly at write time instead of surfacing as mystery bugs at read time, months later.
- Transactions. Move money between accounts, or create an order plus its line items, and either everything commits or nothing does. Correctness under concurrency is handled.
- Joins. You can ask questions across your data — "users who bought X but not Y" — without having planned for that question when you designed the tables.
- Constraints. Foreign keys, uniqueness, and checks turn whole categories of data corruption into impossible states.
The classic knock on SQL — "the schema is rigid" — is mostly outdated in practice. Migrations are routine, tooling is mature, and modern Postgres has a jsonb column type when parts of your data genuinely are free-form, giving you document-style flexibility inside a relational database.
What NoSQL actually buys you
"NoSQL" is an umbrella over very different tools, and being precise about which one you mean is half the decision:
- Document stores (MongoDB, Firestore) hold nested JSON-like documents. Great when each record is naturally self-contained and read whole — and Firestore in particular earns its place in mobile through realtime sync and offline support, which is a platform feature, not a data-model one.
- Key-value stores (Redis) are blazing fast for lookups by key. Nearly always the right cache or session store — and nearly always wrong as your primary source of truth.
- Wide-column stores (Cassandra and friends) exist for write volumes and horizontal scale that most apps will never see.
The honest common thread: NoSQL shines when your access patterns are known, simple, and read-heavy, and when scale genuinely demands distribution. The costs appear later — the queries you didn't plan for require restructuring or duplicating data, relationships pushed into application code become your bugs, and the write-time validation the schema used to do now has to live in every code path that touches the data.
The failure mode in each direction
Choosing document storage for relational data is the more common mistake: an app with users, orders, products, and payments — data that's all relationships — ends up denormalized into documents, with consistency maintained by hand. It works at first and decays as features multiply.
The reverse mistake is rarer but real: forcing something like high-churn realtime presence data, or a pile of heterogeneous third-party payloads, through rigid tables and migrations when a document or key-value model fits naturally.
A default, and reasons to deviate
For a typical app backend — users, content, transactions, relationships — default to Postgres. It handles data models it wasn't stereotypically "for" (JSON, full-text search, even vectors via extensions), scales further than most products will ever need, and keeps every question about your data answerable with a query.
Deviate for specific, articulable reasons: Firestore because you want its offline sync out of the box; Redis beside your main database for caching and sessions; a document store because your records truly are independent blobs; a distributed store because you've measured your way past a single primary. "Deviate for a reason" also means you can mix — Postgres as the source of truth with Redis in front is the boring, battle-tested shape of most successful backends.
The takeaway
Ask two questions. Is your data relational — do records reference each other, and do you care that those references stay correct? And do you actually have the scale problem NoSQL solves, or do you just like the vibe? For most apps the answers are yes and no — and that's a relational database. Pick boring, add specialized tools when a real need shows up, and your database will be the least of your problems.