Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 3

Monolithic Scaling Limitations

Recommended resources

Welcome back! In our last lesson, we dissected the internal structure of a monolith, identifying low cohesion and high coupling as the root causes of code that is difficult to maintain and test. We saw how tangled dependencies and bloated functions create a fragile "big ball of mud."

Today, we will zoom out from the code level to the system level and see how those internal design flaws directly impact a system's ability to grow. When your application's user base grows from thousands to millions, you need strategies to handle the increased load. This lesson focuses on the two fundamental scaling strategies—vertical and horizontal—and, more importantly, exposes their limitations when applied to a traditional monolithic architecture. This will build the foundation for understanding why new architectural patterns like microservices became necessary.

Two Flavors of Scale: Up vs. Out

Before we discuss the limitations, let's clearly define the two primary ways to scale a system. Imagine your application is a restaurant. As it gets more popular, the kitchen gets overwhelmed. You have two choices:

  1. Vertical Scaling (Scaling Up): Upgrade your single kitchen. Buy bigger ovens, hire a world-class, faster chef, and expand the pantry. You're making your one kitchen more powerful.
  2. Horizontal Scaling (Scaling Out): Keep the original kitchen as is, but open identical new branches of the restaurant across the city. You're adding more kitchens to distribute the customers.

This short video provides a concise technical definition of these two approaches.

Vertical Vs Horizontal Scaling: Key Differences You Should Know

Watch this video from ByteByteGo to get a clear overview of vertical and horizontal scaling.

Pay attention to the definitions and the list of advantages and disadvantages for each approach. Start with the explanation of vertical scaling. Then, watch the explanation of horizontal scaling.

Vertical vs. Horizontal Scaling
This diagram provides a simple visual summary. Vertical scaling ("Scale Up") involves making a single server more powerful. Horizontal scaling ("Scale Out") involves adding more servers to a pool.

The Limits of Vertical Scaling

Vertical scaling is the most straightforward approach and is often the first step teams take. If your Django monolith is slow, moving it from a 4-core server to a 16-core server is a simple and effective solution—up to a point. This strategy has fundamental limitations.

Scaling Systems the Right Way

This article provides a clear breakdown of the pros and cons of vertical scaling.

Read the section titled "Vertical Scaling: The Power of Scaling Up" and the list under "Disadvantages of Vertical Scaling." Focus on why this approach is described as a "strong but fragile pillar."

As the article highlights, vertical scaling has three major limitations:

  1. A Hard Ceiling: There is a physical limit to how powerful a single machine can be. You will eventually hit a point where there is no larger server to upgrade to.
  2. Exponential Cost: The cost of high-end enterprise hardware doesn't increase linearly. A server that is twice as powerful can be many times more expensive. At some point, scaling up becomes economically unfeasible.
  3. Single Point of Failure (SPOF): This is the most critical drawback. Your entire application runs on one machine. If that server experiences a hardware failure or needs to be taken down for maintenance, your entire service goes offline. It offers no redundancy or fault tolerance.

The Hidden Limitations of Horizontally Scaling a Monolith

Horizontal scaling seems like the perfect solution to the problems of vertical scaling. It provides redundancy and, in theory, limitless scalability. If you need more capacity, you just add another server. However, when you apply this strategy to a traditional, tightly-coupled monolith, you run into a new set of challenging limitations.

1. The "All-or-Nothing" Problem

A monolithic application is a single, large deployable unit. When you scale it horizontally, you are running multiple identical copies of the entire application.

Imagine your e-commerce monolith has three main functions:

  • A lightweight landing page.
  • A moderately intensive Product Catalog service.
  • A very CPU-intensive Video Processing feature for seller uploads.

If traffic to the landing page spikes, you have to add more servers. But each new server also carries the heavyweight Video Processing code, consuming memory and resources even if it's not being used. You can't scale just the part of the application that needs it; you have to scale everything together. This is inefficient and costly.

2. The Database Bottleneck

This is often the most significant barrier to scaling a monolith. While you can easily run many instances of your stateless application code on multiple servers, they almost always all connect to a single monolithic database.

Even with multiple application servers, all write traffic funnels into a single primary database, which becomes the new bottleneck.

As you add more application servers, the database becomes overwhelmed with connections and queries. At this point, you haven't solved your scaling problem; you've just moved the bottleneck from the application layer to the data layer.

Scaling a relational database is notoriously difficult. The following article provides an excellent overview of the strategies and their complexities.

Scaling Monoliths: A Practical Guide for Growing Systems

This blog post by Milan Jovanović dives deep into database scaling challenges.

Read the entire section titled "Database Scaling," including the subsections on "Read Replicas," "Materialized Views," and "Database Sharding." As you read, notice how each solution introduces new trade-offs, like replication lag (eventual consistency) or immense architectural complexity (sharding).

As you can see, scaling the database for a monolith isn't a simple fix.

  • Read Replicas help with read-heavy workloads but do nothing to alleviate write pressure on the primary database.
  • Sharding (partitioning your data across multiple databases) can provide massive scale but is incredibly complex to implement correctly within a monolithic application, as it often requires significant code changes to handle cross-shard queries and transactions.

A More Sophisticated Approach: Scaling the Modular Monolith

The limitations we've discussed are most severe in a classic "big ball of mud" monolith. However, if you've followed the principles from our last lesson—high cohesion and low coupling—to build a modular monolith, you gain more sophisticated scaling options.

In a modular monolith, even though the code is deployed as a single unit, it's internally organized into well-defined, independent modules. This structure allows for more intelligent horizontal scaling.

The following video demonstrates how you can configure a load balancer to route traffic for specific business functions to dedicated pools of servers, even though every server runs the same monolithic code.

Scaling Monolithic Applications

This video from CodeOpinion explains how a modular monolith can be scaled more granularly.

Watch the section from explaining load balancing to splitting traffic. Notice how the load balancer can use rules (like the URL path) to send traffic for a specific context (e.g., C1) to one group of servers, while traffic for other contexts (C2, C3, C4) goes to another group. This allows you to scale the server pool for a "hotspot" in your system independently.

This approach is a powerful way to extend the life of a monolith. It allows you to address resource contention by isolating workloads. However, it's a workaround, not a fundamental solution. You are still deploying the entire codebase everywhere, and all modules likely still share the same database schema, which remains a potential bottleneck.


Conclusion

In this lesson, we explored the physical and economic walls you hit when trying to scale a monolithic system.

Key Takeaways:

  • Vertical Scaling is simple but limited by cost, the physical ceiling of hardware, and its nature as a single point of failure.
  • Horizontal Scaling offers redundancy but is inefficient for a typical monolith. You must scale the entire application at once, leading to wasted resources.
  • The database is the ultimate bottleneck for a scaled monolith. Scaling a single database is complex, costly, and introduces significant architectural trade-offs like eventual consistency or the complexity of sharding.
  • A well-structured modular monolith can mitigate some horizontal scaling issues by allowing for intelligent traffic routing to isolated server pools, but it doesn't solve the core problems entirely.

These scaling limitations, combined with the code-level issues of coupling we saw previously, create immense pressure on growing systems. But there's another dimension to this problem: people. As an engineering team grows, trying to have dozens or hundreds of developers work on a single, massive codebase creates its own set of bottlenecks.

In our next lesson, we will examine these team scaling challenges and understand how monolithic architecture can slow down development, hinder ownership, and increase cognitive load on your team.