Scaling the Databases: Choosing the right strategy

By Pradyumna Chippigiri

December 11, 2025


When I was working as a junior software engineer in my early days, I’d sit in meetings where people casually talked about “scalability” of our systems. I honestly thought it just meant “Can our database survive more traffic without crashing?” and left it at that.


Over time I realized people were really asking a deeper question:


As the business and the app grow, can the database not only survive, but also keep up and stay fast?

Scalability isn’t only about scaling up when demand spikes. Good systems also let you scale down when traffic is low, so you’re not burning money on overkill infrastructure at 2 a.m.


When a database can’t scale, it usually fails in a few predictable ways:


In this article, we’ll walk through the core horizontal and vertical scaling strategies.


By the end, you should have a clear mental model of when to use each approach and how they fit together as your app and traffic grow.

What is Vertical Scaling?

Vertical scaling


Challenges in Vertical Scaling


What is Horizontal Scaling?

Horizontal scaling


The natural question for anybody to ask would be (which even I asked myself), why would horizontal scaling in databases show up like that and why not similar to horizontal scaling in backend services? And that’s a very good question.


For backend services, horizontal scaling is simple: you add more app servers behind a load balancer, and any server can handle any request. The servers are mostly stateless, so it doesn’t really matter which one you hit.


Databases are different because they store the actual truth of your data.


You can’t just spin up 5 separate database servers and let all of them accept writes on their own. Very quickly, they would disagree about the data. We want the database to be consistent, meaning every user and every server should see the same truth.


So when we add more database servers, we have to decide how they share data and who is allowed to do what, and while scaling a database we should scale both the reads and the writes:


Horizontal scaling for databases is still “add more servers,” but always with a clear plan for who stores what and who handles which reads/writes.

Horizontal Scaling: Read Replicas

Read replicas are a standard way to scale reads from a database.


In most real-world systems, the read:write ratio is heavily skewed towards reads. So instead of letting one database handle both reads and writes, we separate them:

This way, we can scale reads independently by adding more replicas as traffic grows.

How do reads and writes go to different databases?

This does not happen automatically at the database level.


The application / API layer is responsible for routing:

Just for example in the application layer it may look like this:

import psycopg2
from contextlib import contextmanager

# Primary (read + write)
PRIMARY_DSN = "dbname=app user=app_user password=secret host=primary-db"
# Read replica (read-only)
REPLICA_DSN = "dbname=app user=app_user password=secret host=replica-db"

@contextmanager
def primary_conn():
    conn = psycopg2.connect(PRIMARY_DSN)
    try:
        yield conn
    finally:
        conn.close()

@contextmanager
def replica_conn():
    conn = psycopg2.connect(REPLICA_DSN)
    try:
        yield conn
    finally:
        conn.close()

// ---------- Business logic layer ----------

def get_user_by_id(user_id: int):
    """READ → goes to replica"""
    with replica_conn() as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT id, name, balance FROM users WHERE id = %s", (user_id,))
            return cur.fetchone()

def update_user_balance(user_id: int, amount: float):
    """WRITE → goes to primary"""
    with primary_conn() as conn:
        with conn.cursor() as cur:
            cur.execute(
                "UPDATE users SET balance = balance + %s WHERE id = %s",
                (amount, user_id),
            )
        conn.commit()

But what about consistency?

Good question: if all writes go to the primary, how do replicas see the latest data?


Changes made on the primary must be replicated to the replicas. This is done through replication, and there are two common modes:

1. Synchronous replication

Synchronous replication


Pros:

Cons:

2. Asynchronous replication

Asynchronous replication


This is what most production systems use by default.


Pros:

Cons:

This is the trade-off: strong consistency vs. performance and availability.

Horizontal Scaling: Sharding


Problem: We already used read replicas to scale reads. But if one primary node still has to handle all writes, it becomes a bottleneck.


Idea: Split the data horizontally across multiple databases (shards), so writes are spread out instead of hitting a single node.


A shard is just a subset of your data stored in a separate database. Example:

You can (and usually should) have replication inside each shard:

So you end up with: sharding for scaling writes, replication for high availability + read scaling.


The decision of which shard to hit is made in the API / application layer. We define a routing strategy in our code, and every read/write request goes through that logic.

function getShardId(userId: number) {
  return userId % 4; // 0,1,2,3 → 4 shards
}

function getShardConnection(userId: number) {
  const shardId = getShardId(userId);
  return shardConnections[shardId]; // pick the right DB client
}

For more complex setups, instead of a formula we use a shard map / lookup table:

In both cases, the router is just application logic (or a small routing service) that maps:


business key → shard id → DB connection

This brings us to the end of this article. I hope you were able to take away something useful and build a clearer mental model of how databases scale.


In the next part, we’ll go much deeper into sharding: we’ll look at different sharding strategies, how sharding compares to partitioning, and explore even more patterns to scale reads and scale writes as your system grows.


If you liked this article then please do Subscribe to my weekly newsletter!