SQL vs NoSQL
Adda's data model is finally firming up, and Ria has to answer the question she has dodged for months — which database? Shuvo joins to make her answer it on purpose.
The problem
Adda survived the university spike. The load balancer spreads traffic, the cache soaks up the hot posts, the CDN serves images from the edge. But under all of it is the same little SQLite file Ria started with under her desk, and it is groaning. Users, posts, replies, likes, follows — the data has shape now, and that shape matters.
So Ria hires Shuvo, a data engineer, and Shuvo's first question over chai is the one Ria has been dodging for months: "Which database are we actually building on?" Ria reaches for the answer everyone repeats — "SQL is old, NoSQL scales" — and Shuvo stops her. Pick this on vibes, and six months from now it is the one thing you cannot easily undo. This lesson is about making that fork on purpose, by looking at the shape of Adda's data and how it reads it, not at which database is trendy.
A first attempt
The naive move is to pick by scale: "Adda might get big, so NoSQL." But scale is rarely your first problem — your first problem is modeling, and Shuvo proves it with a throwaway example on the whiteboard: an e-commerce order.
Say you store an order as a relational schema. An order has a customer, line items, a shipping address, and payment status. In a relational store that is four tables tied by foreign keys. To render one order page you join them:
SELECT o.id, c.name, i.sku, i.qty
FROM orders o
JOIN customers c ON c.id = o.customer_id
JOIN line_items i ON i.order_id = o.id
WHERE o.id = 4711;That is clean and correct. But every read touches multiple tables, and at very high volume across many machines those joins become expensive — a distributed join can fan out to every shard. Meanwhile the document view stores the whole order as one JSON blob and reads it in a single lookup — fast, but now the customer's name is copied into every order, and changing it means rewriting many documents.
Neither is "better," Shuvo says. They optimize for opposite things — and Adda's real question is which opposite fits its reads.
The insight
Pick the model that matches your access pattern, not your data in the abstract.
If you read data by following relationships in many directions ("all posts by this user," "all users in Dhaka," "top posters this week"), a relational store's joins and ad-hoc queries pay off. If you almost always read one self-contained thing by its id ("give me this user's profile," "this post"), a document or key-value store hands it to you in one hop and scales horizontally without join pain.
The other half of the choice is schema-on-write vs schema-on-read. SQL validates structure when you write (every row must fit the columns). NoSQL often defers structure to when you read (each document can differ). Rigidity is a feature when data must stay consistent; flexibility is a feature when the shape is still moving — and Adda's feature set is still very much moving.
How it works
Classify your data's relationships
Is it highly connected (users, follows, likes, feeds that must reconcile), or a bag of independent records (session tokens, event logs, image metadata)? Connected data leans relational; independent records lean key-value or document.
Match the store type to the pattern
Relational (Postgres, MySQL) for joins, transactions, and ad-hoc queries. Document (MongoDB) for nested, self-contained records. Key-value (Redis, DynamoDB) for lightning lookups by a single key — the same family Adda's cache already uses. Wide-column (Cassandra) for massive write-heavy time series. Graph (Neo4j) when the relationships are the query ("friends of friends").
Decide where the schema lives
Schema-on-write gives you validation and safe migrations at the cost of flexibility. Schema-on-read lets each record differ, pushing the burden onto every reader to handle missing or changed fields.
Plan the escape hatch
Most real systems are polyglot: Postgres as the source of truth, Redis in front for hot reads (Adda is already halfway there), an event log in Kafka. Choosing one database first does not forbid adding others later.
Concrete numbers
A single-key lookup in a well-tuned key-value store returns in well under 1 ms and a single node handles 100k+ reads/second. A 4-table join on an indexed relational database is often 1–10 ms — fine until you shard, where a cross-shard join can balloon to hundreds of ms because it waits on the slowest node.
Storage tells the same story in reverse. Denormalizing the display name into 10 million posts wastes space and turns a one-row name change into a 10-million-document rewrite. Normalizing keeps it as one row but costs a join on every read. You are trading write cost for read cost — and Shuvo wants Adda to trade with its eyes open.
When to use it
The trade-off
SQL buys you consistency, joins, and ad-hoc queries, and charges you flexibility and easy horizontal scale. NoSQL buys you flexible schemas and horizontal scale, and charges you joins, multi-record transactions, and duplicated data you must keep in sync yourself. There is no free option — only the one that fits your reads.
Default to relational
When genuinely unsure, start with a relational database. It handles the widest range of queries, gives you transactions for free, and modern Postgres also stores JSON documents — so you rarely lose the NoSQL option, but you keep joins when you need them. Adda's core lands on Postgres for exactly this reason.
Practice
Recap
- Choose by access pattern and data shape, not by which database is fashionable.
- SQL trades flexibility and easy scale for joins, transactions, and ad-hoc queries; NoSQL makes the opposite trade.
- Schema-on-write validates early; schema-on-read stays flexible but pushes the burden onto readers.
Indexing
How either kind of store finds a row without scanning everything.
Sharding
Splitting data across machines when one is not enough.
Transactions
The 'all or nothing' guarantee relational stores give you.
In an interview
State the fork out loud: "What are the dominant queries?" Then reason from access patterns, not brand names. Say "relational for the transactional core, a key-value cache for hot reads" — showing you pick per-workload and think polyglot scores far higher than declaring one database the winner.
How is this guide?
Last updated on
Caching Fundamentals
Adda's database is melting under a flood of identical reads for the same hot posts. Ria and Tanvir stop re-answering the same question — they answer it once and remember the answer.
Indexing
Adda's login page starts timing out, and Shuvo finds the database reading every row to find one. Finding a needle in millions by checking each straw is hopeless — an index is the book's table of contents for your data.