← back

The Question Nobody Asks Before Choosing a Database.

July 07, 2026·13 min read·distributed systems

A practical introduction to data modelling: why different databases exist, how read and write trade-offs shape them, and how to choose the model that fits the problem instead of copying whatever the tutorial used.

00 - the suit problem

A tuxedo is perfect for a wedding and ridiculous for a hike. A wetsuit keeps you alive surfing and gets you laughed out of a boardroom. A raincoat won’t replace a blazer.

None of these are objectively better. They solve different problems for different bodies in different situations. The question is never which is best - it’s which fits this.

Data models work exactly the same way.

And yet most engineers I know - myself included, for an embarrassingly long time - just reached for whatever database the tutorial used. Postgres if you did a web course. MongoDB if you followed a YouTuber. Redis because someone on Twitter said your app was slow. We picked databases like we picked fonts. Vibes and familiarity, not reasoning.

The question nobody stops to ask is the most obvious one: why are there so many databases if all they do is read data and write data?

That question, taken seriously, is the entire field of data modelling.


01 - the two jobs that fight each other

A database has exactly two jobs.

Write data when something happens. Read it back when someone asks.

That’s it. And yet these two jobs pull in opposite directions - in a way that isn’t obvious until you think about it carefully.

Imagine the simplest possible database: a text file. Every time something happens, you append a line, be it a new user sign up or some information update.

Writes are incredibly fast. But now someone asks for this user’s current email. You read the whole file, find every mention of this user, determine the latest one. The file grows forever. Reads get slower forever.

So you try the opposite. You build carefully organized structures - sorted, indexed, pages laid out so lookups are instant. Reads are now fast. But every write has to update the primary data, update every index, maintain metadata, and do all of this safely in case of a crash.

Every database in existence is a bet somewhere on this spectrum. That bet shapes how data gets organized, how queries work, and what the system is actually good at. But storage is only half the story.

The other half sits one level above it: what shape is your data, and how do you need to query it?


02 - what a data model actually is

A data model is the set of rules that determines how information is represented - not just stored. It answers: what is the unit of data, how do units relate, and what questions can you ask?

Data modelling is the process of taking your actual problem - users, orders, messages, locations -and figuring out which representation fits it best.

The model you choose doesn’t just affect your database. It affects your schema, your indexes, your query patterns, your application code, and your ability to scale. It’s shaped by three factors:

Data volume - how much are you storing, and does it fit on one machine? A single Postgres instance handles a remarkable amount. A billion events per day does not. Volume determines whether you’re looking at a single-node database or a distributed one.

Access patterns - how is the data actually queried? By ID? By time range? By relationship traversal? By aggregating across millions of rows? This is the most important factor. The access pattern determines what indexes you need and which model makes those queries cheap or expensive.

Consistency requirements - this is the ACID vs BASE divide. ACID databases - Atomicity, Consistency, Isolation, Durability - guarantee correctness at every step. A write either happened completely or not at all. BASE systems - Basically Available, Soft state, Eventually consistent - prioritise availability and accept that different nodes might briefly disagree.

A bank transfer needs ACID. A like count being two seconds stale is completely fine. These are fundamentally different problems.

Get these three factors right and the choice of database becomes obvious.


03 - relational: the world as a spreadsheet

PostgreSQL

Data lives in tables. Tables have rows and columns. Columns have types. Relationships between tables are expressed via foreign keys. You query everything with SQL.

The promise is consistency. If your schema says a user must have an email, every user has an email. If an order must reference a valid user, orphaned orders can’t exist. Full ACID guarantees. You can sleep knowing your data is sane.

Schema design in the relational model is an upfront investment. You normalise - split data into tables to eliminate duplication, then join at query time. The schema is your data contract.

One thing worth saying clearly: modern Postgres covers more ground than its reputation suggests. JSONB columns, generated columns, full-text search, PostGIS for geospatial, foreign data wrappers - a lot of decisions to reach for MongoDB were made five years ago and would be “just use JSONB in Postgres” today. The gap between relational and document has genuinely narrowed for a large class of problems.

The cost is rigidity under change. Evolving a schema means migrations - versioned operations that touch every row. The smell is tables full of nullable columns or JSON blobs in Postgres, which is a sign you’ve outgrown the model for that part of your data.

Fits: Financial systems. E-commerce. Inventory. Anything where wrong data has real consequences. Stable schemas. Strong relationships.

Struggles: Deeply irregular data. Rapidly changing schemas. Write volumes that exceed what a single node can handle cleanly.


04 - document: the world as a notebook

MongoDB

The document model starts from a different premise: what if the unit of data is a self-contained object, not a normalised row?

A document is typically JSON. It can be nested - a user document contains an array of addresses, each with their own fields. No joins required. Everything about an entity lives in one place… i like to think if computers maintained diaries this is what whey would be.

{
  "id": "user_001",
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "addresses": [
    {
      "type": "home",
      "city": "London",
      "street": "12 St James's Square"
    },
    {
      "type": "work",
      "city": "London",
      "street": "1 Dorset Street"
    }
  ],
  "preferences": {
    "theme": "dark",
    "notifications": true
  }
}

In Postgres, this is three tables and a join. In MongoDB, it’s one read.

The promise is flexibility and locality. Data that belongs together is stored together. The schema is implicit - add a field to new documents and old documents without it still work.

The schema design question in document databases is: what gets embedded and what gets referenced?

Embed when data is accessed together and doesn’t need to exist independently. Reference when data is shared across documents or queried on its own. Get this wrong - embedding things that should be referenced - and you paint yourself into a corner. You need to query all comments by a specific user across all posts? Too bad, you embedded them inside posts. The flexibility is real but it accumulates eventual consistency debt - shortcuts that feel free early and charge interest when your query patterns shift.

The other cost is relationship integrity across documents. Nothing enforces it. Delete a user and their ID might still live inside thousands of order documents pointing at nothing. Your application owns that contract now.

Fits: Content management. Product catalogs. User profiles. Hierarchical data. Fast early iteration.

Struggles: Strong relational consistency. Querying across document types. Data that’s more interconnected than it first appears.


05 - key-value: the world as a dictionary

Redis · DynamoDB · etcd

A key. A value. Store it. Retrieve it by key. That’s the entire contract.

No schema. No relationships. No query language. No joins.

SET  session:user_1001   "eyJhbGciOiJIUzI1NiJ9..."   EX 3600
GET  session:user_1001   →  "eyJhbGciOiJIUzI1NiJ9..."
INCR rate_limit:ip:192.168.1.1  →  43

Redis is the dominant in-memory example, but the model is broader. DynamoDB is a key-value and document store built for near-infinite horizontal scale - Amazon’s bet that they could trade SQL expressiveness for single-digit millisecond latency at any volume. etcd is a strongly consistent key-value store that Kubernetes uses as its source of truth for cluster state. Same fundamental model, very different engineering choices underneath.

What they share: if you know the key, the answer is instant. If you don’t, you have a problem. The value is opaque - the database can’t query inside it. “Find all users who signed up in January” is unanswerable without already maintaining a key for it. Schema design here lives entirely in your key naming convention. That discipline is yours to maintain.

Fits: Caching. Session management. Rate limiting. Leaderboards. Distributed config. Anything with natural expiry and known-key access patterns.

Struggles: Anything that isn’t a direct lookup. Being the sole source of truth for complex relational data.


06 - graph: the world as a network

Neo4j

When relationships are the point - when the interesting question is “how does this connect to that, through what, and how many hops?” - SQL starts to fight you. Graph databases model data as nodes and edges. Both can have properties.

A six-hop relationship query in SQL compounds with every join. In a graph database, edges are stored as direct physical references - traversal depth has roughly constant cost.

One honest caveat: for moderate-scale graph problems, Postgres’s recursive CTEs (WITH RECURSIVE) handle more than people expect. Follower relationships, org hierarchies, category trees - if your graph is modest and you’re already on Postgres, try the recursive CTE before adding infrastructure. The graph database earns its place when the graph is deep, dense, and traversal is your primary query pattern.

Fits: Social graphs. Fraud detection. Recommendation engines. Network topology. Anything where the path matters as much as the destination.

Struggles: Bulk aggregates. Simple CRUD. Anything that doesn’t require traversal.


07 - two more you’ll definitely encounter

Four models cover most of what people build day to day. Two more show up constantly in production systems and deserve more than a footnote.

Wide-column stores (Cassandra, ScyllaDB). These look like relational tables but are built for distributed writes from the start. Data is organised by partition key first, then sorted within partitions. The key constraint: your query patterns must be known at schema design time. You design tables around the queries, not the data. Change your query pattern and you often need a new table. Discord’s message storage is the canonical example - billions of messages, designed around one query: “give me the last N messages in channel X.”

Columnar and time-series databases (ClickHouse, BigQuery, Timescale). Instead of storing each row together, these store each column together. Scanning one column across millions of rows becomes extremely fast. Updating individual rows becomes very slow. “What was the average response time for every API endpoint over the last 30 days?” is a natural query here and an unpleasant one for Postgres. If you’re building analytics dashboards or time-series systems and Postgres feels wrong, this is usually why. These databases exist because that problem genuinely needs a different model.


08 - the triangle

No model is good at everything. Every model maximises something by giving something else up.

Relational sits toward consistency. Enforced rules, guaranteed integrity, correctness as a database property rather than an application property.

Document sits toward flexibility. Schema-optional, fast to iterate, hierarchical access. Trades consistency guarantees for development speed.

Key-value sits toward performance at scale. Pure speed, minimal overhead, horizontal distribution. Trades almost all query expressiveness for microsecond access.

Graph and columnar databases don’t compete cleanly on these axes - they’re specialists, optimised for a specific query shape and genuinely wrong for everything else. They sit outside the triangle.

The reason there are so many databases is that this triangle has no winner. Every real application lands at a different point - and most serious applications land at multiple points simultaneously.


09 - what this looks like in the real world

Instagram - users and posts in Postgres. Follower graph at hundreds of millions of users built on specialised graph infrastructure. Likes and view counts in Redis. Stories in object storage. One product, three or four models.

Uber - driver and rider profiles in Postgres. Live driver locations in a key-value store, updated every few seconds for millions of drivers simultaneously - no relational database survives this write rate. Geospatial dispatch via PostGIS or purpose-built systems. Payments in Postgres with ACID, full stop.

Discord - messages in ScyllaDB (wide-column), designed around one query pattern. Server and membership data in Postgres. Message search in Elasticsearch. Presence data in Redis. Four models, one application, each doing what it was built for.

These case studies are short on purpose because these are such deep topics that they deserve a whole blog of their own.

"Most serious systems end up using multiple databases because different parts of the application have genuinely different needs. However, every additional store adds operational cost. A Redis cache in front of Postgres is usually worth it. A dedicated graph database for something two recursive CTEs could handle usually isn’t."


10 - the questions that actually matter

Instead of “which database should I use?” ask these:

What is the unit of data? A row? A document? A key and an opaque blob? A node? A time-stamped event? The shape points strongly at a model.

What are the dominant operations? Lookups by ID? Range queries by time? Relationship traversal? Bulk aggregates across millions of rows? Each favours a different model.

What’s the volume, and does it grow horizontally? Single-machine databases are simpler to operate. Distributed scale changes which options are realistic.

How wrong can you afford to be? ACID or eventual consistency? This single question eliminates half the options for most problems.

How stable is your schema? Changing weekly - flexibility matters. Stable for years - rigidity is a feature, not a bug.

What becomes expensive in this model? In relational: deep joins at scale. In document: cross-document queries and schema drift. In key-value: anything that isn’t a direct lookup. In graph: bulk aggregates. In columnar: point reads and updates. Know the expensive query before you’re in production with it.


11 - back to the suits

The best data model isn’t the fastest, the most popular, or the newest. It’s the one whose trade-offs match your problem.

Relational makes consistency easy and schema evolution hard. Document makes iteration easy and cross-document consistency hard. Key-value makes lookup speed easy and any other kind of query hard. Graph makes traversal easy and everything else harder. Columnar makes aggregates over large datasets easy and point reads hard.

Don’t chase the best database. Learn to read a problem - its volume, its access patterns, its consistency requirements - and recognize which trade-offs you can actually afford. Then pick the model that makes the right things cheap.

Next time you reach for Postgres out of habit, stop for thirty seconds. Ask what shape your data is, how you need to query it, and what happens when this grows by a factor of ten. The answer might still be Postgres. It usually is. But at least now you know why.