SQL or NoSQL?
Imagine two ways to keep customer records. One office uses a filing cabinet with forms: every customer has a form with the same fields, orders are on separate forms that say which customer they belong to, and a clerk can answer any question (“all customers in Novi Sad who ordered last week”) by cross-referencing. The other office keeps one folder per customer with everything inside: ask for a customer and you get the whole folder instantly, but “everyone who ordered last week” means opening every folder.
The first is a relational (SQL) database. The second is a document database, one kind of NoSQL database. Neither is better; they’re good at different questions.
The choice is about access patterns, consistency and scale, not fashion. Relational databases give you a normalized model, ad hoc queries, joins, constraints and multi-row ACID transactions, and scale vertically and with read replicas a long way. NoSQL stores trade query flexibility for predictable performance at scale on known access patterns: you design the data around the queries (denormalized, partitioned by key), often with weaker consistency or transaction scope. PostgreSQL with JSONB covers many “we need a document store” cases.
| Type | Examples | Good at | Weak at |
|---|---|---|---|
| Relational | PostgreSQL, MySQL | Related data, any query, transactions, constraints | Massive write scale on one node |
| Document | MongoDB, Firestore | Reading/writing whole objects; flexible shape | Joins and queries across many documents |
| Key-value | Redis, DynamoDB | Very fast lookups by key, huge scale | Anything except lookups by key |
| Wide-column | Cassandra, ScyllaDB | Huge write volume, time series, partitioned by key | Ad hoc queries, transactions |
| Search | Elasticsearch, OpenSearch | Full-text search, filters, ranking | Being the source of truth |
-- Relational: separate tables, linked by keys
CREATE TABLE customers (id BIGINT PRIMARY KEY, name TEXT NOT NULL, city TEXT);
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES customers(id),
created_at TIMESTAMPTZ NOT NULL,
total_cents INT NOT NULL
);
CREATE TABLE order_items (
order_id BIGINT REFERENCES orders(id),
product_id BIGINT NOT NULL,
quantity INT NOT NULL
);
-- Any question, by joining:
SELECT c.city, SUM(o.total_cents)
FROM orders o JOIN customers c ON c.id = o.customer_id
WHERE o.created_at > now() - interval '7 days'
GROUP BY c.city;{
"_id": "order-1001",
"customer": { "id": 42, "name": "Ana", "city": "Novi Sad" },
"createdAt": "2026-11-08T10:15:00Z",
"items": [
{ "productId": 7, "name": "Mug", "quantity": 2, "priceCents": 950 }
],
"totalCents": 1900
}In the document version, showing an order is one read. But the customer’s name is copied into every order: if Ana changes her name, every order document has the old one (sometimes that’s what you want, like on an invoice; sometimes it isn’t). In the relational version, the name lives in one place, and a join brings it in when needed.
A good default for a new app is a relational database like PostgreSQL. Reach for something else when you have a specific need it can’t meet: enormous write volume, a key-value lookup at huge scale, full-text search.
Pick a database
For each feature of a food delivery app, pick a kind of database and give one reason:
- Orders, payments and refunds
- The courier’s current location, updated every 4 seconds
- Searching restaurants by dish name, with typos (“piza”)
- A user’s session and shopping cart
›
Ask what each feature does most: complex queries and correctness, lots of tiny writes where only the latest matters, text search, or quick lookups by one key.
SolutionTry it yourself first
- Relational (PostgreSQL): money needs transactions (an order and its payment succeed or fail together), constraints, and flexible reporting queries.
- Key-value in memory (Redis), keyed by courier: only the latest position matters, writes are tiny and frequent, and Redis has geo commands for “couriers near me”. History, if needed, goes to cheaper storage asynchronously.
- A search engine (Elasticsearch/OpenSearch): full-text matching, typo tolerance and ranking; it’s fed from the main database and isn’t the source of truth.
- Key-value (Redis) with a TTL: looked up by one key on every request, and losing it occasionally isn’t a disaster.
Real systems use several databases, each for what it’s good at, with one clear source of truth per piece of data.
Modeling for NoSQL
NoSQL modeling starts from the query list, not the entities: “get order by id”, “list a customer’s orders newest first”, “all orders for a restaurant today”. Each query maps to a key design: a partition key (which node) and a sort key (order within the partition), with data denormalized so each query reads one partition. That’s how DynamoDB single-table designs and Cassandra tables work: a new query often means a new table or index, populated by duplicating writes. It scales predictably, and it makes unplanned queries expensive, which is the honest trade-off to state in an interview.
Deep diveACID, BASE and what 'NoSQL has no transactions' really meansInterview favorite
ACID (atomicity, consistency, isolation, durability) describes relational transactions: several rows change together or not at all, and concurrent transactions don’t see each other’s half-done work, at a configurable isolation level (PostgreSQL defaults to READ COMMITTED, not SERIALIZABLE). BASE (“basically available, soft state, eventually consistent”) described early distributed stores that gave up cross-node transactions for availability. Today the line is blurry: MongoDB and DynamoDB have multi-document transactions (with limits), and distributed SQL databases (CockroachDB, Spanner, YugabyteDB) offer serializable transactions across nodes at higher latency. The useful interview answer names the transaction scope you need (“these three writes must be atomic”) and picks a store that provides it.
- Choose by access patterns, transaction scope and scale. Relational (PostgreSQL) is the default: joins, constraints, ad hoc queries, ACID; JSONB covers many document needs.
- Document/key-value/wide-column: data modeled around known queries (partition key + sort key, denormalized); unplanned queries are expensive.
- Search engines and caches are derived stores, never the source of truth.
- Polyglot persistence is normal; keep one source of truth per datum and sync derived stores (CDC, outbox).
Check this part
Orders and payments must succeed or fail together, and the business wants flexible reports. Which kind of database?
What is a drawback of copying the customer's name into every order document?
How is data modeled in a store like DynamoDB or Cassandra?