A practical flowchart, the questions that actually determine the answer, and permission to pick Postgres.
Prerequisites: Databases Overview, NoSQL Modeling Time to read: ~20 minutes
🚨 Default to PostgreSQL. Move away from it only when you can name the specific thing it can’t do.
This isn’t laziness — it’s the correct engineering position, and stating it confidently in an interview is a strength, not a weakness. Postgres gives you:
JSONB with indexing — document-store flexibility without a second systempgvector (embeddings), TimescaleDB (time series), Citus (sharding)📐 A well-tuned Postgres on modern hardware handles tens of thousands of writes/second and several terabytes comfortably. Most systems you will ever build never exceed that.
🎙️ “I’d start with Postgres. It covers the relational core, JSONB handles the semi-structured fields, and full-text search is adequate for now. I’d add a specialized store only when a specific workload demonstrably outgrows it — each additional datastore is real operational cost.”
Ask these in order. The answers usually pick the database for you.
🚨 The most common cause of a failed NoSQL adoption is choosing it before the access patterns stabilized. → NoSQL Modeling
Do the estimation before answering.
| Data / write rate | Answer |
|---|---|
| < 1 TB, < 10k writes/s | One Postgres. Not a debate. |
| 1–10 TB, < 20k writes/s | Postgres + read replicas + cache |
| > 10 TB or > 50k writes/s | Shard, or NoSQL, or NewSQL |
| Petabytes, write-dominated | Wide-column (Cassandra, Bigtable) |
Payments and inventory: strong. View counts and recommendations: eventual. This is per-field, and it may mean two databases. → Consistency Models
Relational with many-to-many → relational. Self-contained aggregates → document. Time-ordered events per entity → wide-column or time-series. Relationships are the query → graph.
🚨 The most underweighted factor. A database nobody can debug during an incident is a liability regardless of benchmarks. Managed services (RDS, Aurora, Atlas, DynamoDB) shift much of this, and choosing one is a legitimate senior answer.
flowchart TB
S{Is it relational data<br/>with evolving queries?} -->|yes| P[(PostgreSQL)]
S -->|no| T{What kind?}
T -->|Key lookups only,<br/>ephemeral| R[(Redis)]
T -->|Huge write volume,<br/>known patterns| C[(Cassandra /<br/>ScyllaDB)]
T -->|Self-contained documents| M[(MongoDB or<br/>Postgres JSONB)]
T -->|Time-stamped metrics| TS[(TimescaleDB /<br/>InfluxDB / Prometheus)]
T -->|Traversing relationships| G[(Neo4j)]
T -->|Analytics over<br/>billions of rows| A[(ClickHouse /<br/>BigQuery)]
T -->|Text relevance search| E[(Elasticsearch)]
T -->|Large immutable blobs| O[(S3)]
P -->|outgrew one node?| N{Need SQL + ACID<br/>at horizontal scale?}
N -->|yes| NS[(CockroachDB /<br/>Spanner / Vitess)]
N -->|no| C
| Database | Pick it when | Don’t pick it when |
|---|---|---|
| PostgreSQL | Almost always, to start | Write volume genuinely exceeds one node |
| MySQL | Team knows it; Vitess sharding path | You want Postgres’s richer features |
| Redis | Cache, sessions, rate limits, leaderboards, queues | It’s your source of truth for durable data |
| MongoDB | Documents, rapid iteration, sharded scale | You need cross-document transactions frequently |
| Cassandra / ScyllaDB | Massive writes, time-ordered, known patterns | Ad-hoc queries; delete-heavy; small scale |
| DynamoDB | AWS-native, predictable single-digit-ms, serverless | Query flexibility matters; cost at high read volume |
| ClickHouse | Analytics over billions of rows, fast | Transactional workloads; frequent updates |
| Elasticsearch | Text relevance, faceting, log analytics | Source of truth (it’s a derived store) |
| Neo4j | Deep graph traversal is the core query | General-purpose storage |
| TimescaleDB / InfluxDB | Metrics, IoT, time-range queries + downsampling | Non-temporal data |
| CockroachDB / Spanner / TiDB | SQL + ACID at horizontal scale, multi-region | Cost and latency are dominant concerns |
| S3 / object storage | Blobs, backups, data lake | Anything needing partial updates or low latency |
Orders, payments, inventory → PostgreSQL (ACID is non-negotiable)
Sessions, cart, hot cache → Redis (speed, ephemeral)
Product search → Elasticsearch (relevance, faceting)
Product images → S3 + CDN (size)
Clickstream events → Kafka → ClickHouse (volume, analytics)
🎙️ “Five stores, but each earns its place: transactional integrity, latency, relevance, size, and analytical volume are genuinely different problems. I’d start with Postgres and S3 only, and add the others as each need becomes real.”
Message history → Cassandra (huge write volume, time-ordered, known access)
User profiles, groups → PostgreSQL (relational, modest volume)
Presence / online status → Redis (ephemeral, TTL, very high churn)
Media → S3 + CDN
Message search → Elasticsearch
The split is instructive: messages are the high-volume, append-mostly, partition-by-conversation workload that Cassandra exists for. Users and groups are ordinary relational data at ordinary scale. Using one database for both would compromise one of them.
Sensor readings → TimescaleDB (time-series, compression, downsampling, retention)
Device registry, config → PostgreSQL
Real-time dashboards → Redis / materialized views
Long-term archive → S3 (Parquet)
Everything → PostgreSQL
🚨 This is the correct answer, and giving it takes more confidence than listing six databases. Access patterns aren’t known yet, scale is small, and engineering time is the scarcest resource. Adding datastores early is how small teams end up spending their time on operations instead of product.
Every additional datastore costs you:
⚖️ The rule: each datastore must be justified by a workload the existing ones genuinely cannot serve. “It would be a bit faster” isn’t enough.
| Managed (RDS, Atlas, Aurora) | Self-hosted | |
|---|---|---|
| Ops burden | Minimal | Substantial — you’re on call for it |
| Cost | 2–3× the raw compute | Cheaper in dollars, expensive in engineer-hours |
| Control | Limited (versions, extensions, config) | Full |
| Backups, failover, patching | Automatic | Yours to build and test |
| Scaling | Often a click | Yours to build |
🎙️ The default answer for most teams is managed, and saying so is not a cop-out: “I’d use RDS rather than self-hosting. Automated backups, multi-AZ failover, and patching are things I’d otherwise have to build and — more importantly — actually test. Self-hosting makes sense at a scale where the cost difference funds a dedicated team.”
| Choice | Gain | Cost |
|---|---|---|
| Start with Postgres | Simplicity, flexibility, one system to operate | Write ceiling on one node |
| Add a specialized store | Right tool for a specific workload | Operational multiplication; sync complexity |
| NewSQL | SQL + ACID at scale | Cost, coordination latency, smaller ecosystem |
| Managed service | No ops burden | Cost, less control, lock-in |
| Multiple stores | Each workload optimized | Drift, no cross-store transactions, cognitive load |
1. Justify a real choice. Take a system you’ve built. Write down its access patterns, its actual data volume and write rate, and its consistency requirements per field. Then ask: given those numbers, is the database you chose the right one? Most people find they over- or under-engineered, and the gap is instructive.
2. Find the crossover. Load 10 million rows into Postgres. Benchmark writes with pgbench at
increasing concurrency. Find where throughput plateaus. That number is your real “when would we need
something else?” threshold — and it’s usually higher than expected.
3. Try the same workload two ways. Model a time-series workload in plain Postgres and in TimescaleDB. Compare insert throughput, storage size (compression), and the speed of a time-range aggregate. The difference tells you when a specialized store earns its keep.
4. Price it. For a realistic scenario, compare the monthly cost of: RDS Postgres, DynamoDB on-demand, and a self-hosted cluster on EC2 (including a fraction of an engineer’s salary). Cost arguments carry weight in interviews and almost nobody makes them.