Hello! Welcome back to your system design course.
In our last lesson, we moved from abstract requirements to concrete numbers. We learned how to perform back-of-the-envelope calculations to estimate traffic, storage, and bandwidth. For our running example of a photo-sharing app, we estimated needing to handle around 200 reads per second and plan for nearly 5 petabytes of storage over 10 years.
Today's Goal
Knowing the scale of the system is the first step. The next is deciding on its fundamental structure. Today's lesson addresses the learning outcome: Analyze trade-offs between monolithic, microservices, and serverless architectures for different operational and scaling requirements.
This is one of the most foundational decisions in system design. It's analogous to deciding the manufacturing strategy for a new product: will you build it in one large, integrated factory (a monolith), a network of specialized, independent workshops (microservices), or by renting automated, on-demand manufacturing capacity for specific tasks (serverless)? Each approach has profound implications for cost, speed, and scalability.
Our capacity estimates from the last lesson will be a crucial input into this decision-making process.
1. The Monolith: The All-in-One Application
Let's start with the most traditional approach. A monolithic application is built as a single, unified unit. The user interface, business logic, and data access layers are all combined into one codebase and deployed as a single application.
Most applications begin this way because it's simple and fast to get started.
Monolithic vs Microservice Architecture: Which To Use and When?
To understand the monolith, let's watch this video from Alex Hyett. It clearly explains what a monolith is, its initial advantages, and the problems that arise as it grows.
Watch the first two sections, 'What is a Monolith?' (0:19 - 2:07) and 'Problems with Monoliths' (2:07 - 3:33). Pay attention to the trade-offs between initial development speed and long-term scaling challenges.
As the video highlights, the key characteristics of a monolith are:
- Advantages:
- Simplicity: One codebase is easier to develop, debug, and deploy initially.
- Performance: Communication between components happens in-memory, which is very fast.
- Disadvantages:
- Scaling: If one feature (e.g., feed generation) experiences high traffic, you must scale the entire application by running more copies of it. This is inefficient and costly.
- Development Velocity: As the codebase grows, it becomes complex and tightly coupled. A small change can have unintended consequences, and the entire application must be re-tested and re-deployed, slowing down releases.
For our photo-sharing app, starting with a monolith would allow us to launch quickly. But as we approach our estimated 200 reads/sec, we'd need to run multiple large copies of the entire app, even if the user profile section is barely used. This is the primary pain point that leads teams to consider other architectures.
2. Microservices: Divide and Conquer
Microservice architecture is a direct response to the problems of a monolith. The core idea is to break down a large application into a collection of smaller, independent services, each responsible for a specific business function.
Monolithic vs Microservice Architecture: Which To Use and When?
The same video provides an excellent overview of the microservices approach, including its benefits and the new complexities it introduces.
Watch the sections 'What are Microservices?' (3:33 - 4:14), 'Advantages of Microservices' (5:24 - 7:42), and 'Disadvantages of Microservices' (7:42 - 9:11).
In this model, our photo-sharing app might be composed of:
UserService: Manages user accounts and profiles.ImageUploadService: Handles uploading and storing new photos.FeedGenerationService: Creates the user's home feed.
Key trade-offs for microservices include:
- Advantages:
- Granular Scaling: We can scale the
FeedGenerationServiceindependently to handle the 200 reads/sec, without touching the other services. This is highly efficient. - Team Autonomy: Different teams can own different services, developing and deploying them independently. This increases development speed in large organizations.
- Technology Flexibility: A team could write the
ImageUploadServicein Go for its performance, while theFeedGenerationServiceuses Python for its data science libraries.
- Granular Scaling: We can scale the
- Disadvantages:
- Operational Complexity: You now have many services to deploy, monitor, and manage. Communication happens over a network, which introduces latency and potential failure points.
- Distributed Debugging: A single user request might travel through multiple services. If something fails, tracing the problem across services is much harder than in a monolith.
3. Serverless: The "No-Ops" Approach
Serverless computing takes the idea of abstraction even further. It's an architectural model where the cloud provider is responsible for managing the server infrastructure. You write code as individual functions and the provider automatically provisions resources to run them in response to events.
This video from IBM Technology explains the concept of serverless, how it evolved, and its core benefits and drawbacks.
Watch the segments 'Defining Serverless' (0:00 - 0:28), 'How Serverless Works' (2:33 - 3:32), and 'Benefits and Drawbacks' (3:32 - 6:28). Focus on the concepts of event-driven execution and pay-per-use.
The most common serverless model is Functions as a Service (FaaS).
- How it works: You deploy a piece of code (a function) that is triggered by an event. For our photo app, an "image uploaded" event could trigger a
resizeImagefunction. - Key Trade-offs:
- Advantages:
- No Server Management: You don't provision, patch, or manage any servers.
- Automatic Scaling: The platform automatically scales your functions from zero to thousands of concurrent executions based on demand.
- Pay-per-Use: You are billed only for the exact time your function is executing, which can be extremely cost-effective for infrequent or spiky workloads.
- Disadvantages:
- Vendor Lock-in: Your functions are tied to a specific cloud provider's platform (e.g., AWS Lambda, Google Cloud Functions).
- Cold Starts: If a function hasn't been used recently, there can be a delay (latency) on the first invocation as the platform prepares to run it.
- Statelessness: Functions are generally stateless, meaning they can't store information between executions. State must be managed in an external database or cache.
- Advantages:
4. A Direct Comparison
Now let's put it all together to analyze the trade-offs side-by-side. The following article provides a great summary of these differences.
Monoliths vs Microservices vs Serverless | Blog
This article from Harness, 'Monoliths vs Microservices vs Serverless', directly compares the architectures across the key dimensions we care about: scalability, complexity, and maintenance.
Please read the section titled 'Analysis of Architectural Differences'. It's a short but very dense comparison that directly addresses our learning outcome.
To make this even clearer, here is a summary table based on the GeeksforGeeks article (Resource f7c4c) that consolidates the most important trade-offs.
| Aspect | Monolithic Architecture | Microservice Architecture | Serverless Architecture |
|---|---|---|---|
| Scalability | Poor. Entire application must be scaled as one unit. | Good. Individual services can be scaled independently. | Excellent. Automatically scales based on request load. |
| Development Complexity | Simple initially, but becomes very complex as it grows. | High. Requires handling a distributed system from the start. | Simple for individual functions, but overall system can be complex. |
| Deployment | Simple. One unit to deploy. | Complex. Multiple deployment pipelines and coordination needed. | Simple. Deploy individual functions. |
| Operational Overhead | Low initially, but managing large instances can be costly. | High. Requires infrastructure for service discovery, monitoring, etc. | Very Low. Cloud provider manages all infrastructure. |
| Cost Model | Pay for always-on servers, even when idle. | Pay for always-on servers, but can be optimized per-service. | Pay only for compute time used. Highly efficient for variable loads. |
| Resilience | Low. A failure in one component can bring down the entire app. | High. Failure in one service can be isolated from others. | Very High. Failures are isolated to a single function execution. |
| Vendor Lock-in | Low. Can be deployed on any server. | Medium. Depends on chosen technologies (e.g., message queues). | High. Tied to a specific cloud provider's FaaS platform. |
5. Making the Right Choice
So, which architecture should you choose? As the Alex Hyett video suggests, there's a common and pragmatic path:
- Start with a Monolith: For a new product or startup, the top priority is speed to market. A monolith is the fastest way to build and launch an initial version. You don't have scaling problems yet, so don't pay the complexity price of microservices upfront.
- Migrate When It Hurts: As your application grows and you start experiencing the "pain points"—slow deployments, difficult bug-fixing, and inefficient scaling—you can begin to strategically break pieces off the monolith.
- Adopt a Hybrid Approach: You might extract a computationally intensive, high-traffic component like our
FeedGenerationServiceinto a microservice. At the same time, you could implement a discrete, event-driven task likeresizeImageas a serverless function. Most large, mature systems are hybrids.
This evolutionary approach allows you to choose the right architecture for the right problem at the right time, balancing development speed with long-term scalability and operational cost.
Conclusion
Excellent work today. We've explored the fundamental architectural patterns that underpin almost every modern application.
Key Takeaways:
- Monoliths offer initial simplicity and speed but suffer from poor scalability and become unwieldy over time.
- Microservices provide granular scaling and team autonomy but introduce significant operational and development complexity.
- Serverless offers the ultimate in pay-per-use efficiency and auto-scaling for event-driven tasks, but comes with constraints like vendor lock-in and cold starts.
- The choice is a trade-off between development speed, scalability, operational cost, and organizational structure. There is no single "best" architecture; the right choice depends on the specific requirements and stage of your project.
Preview of the Next Lesson:
We've now seen that moving from a monolith to microservices or serverless means building a distributed system. These systems introduce powerful capabilities but also new challenges. In our next lesson, we will dive into the fundamental trade-offs of all distributed systems by learning to identify and articulate trade-offs between consistency, availability, latency, and scalability. This will give us the vocabulary to discuss core system properties, starting with the famous CAP theorem.
Can't find a good explanation? Sign up and we'll make it for you
Sign up