The monolithWhat is monolith?A software architecture where the entire application lives in a single codebase and deploys as one unit. Simpler to build and debug than microservices.-vs-microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data. debate is one of the most heated in software. You'll find engineers who swear by one and dismiss the other. The truth is neither is universally better, it depends entirely on where you are in your product's life. This lesson gives you the map to make that call yourself.
What a monolithWhat is monolith?A software architecture where the entire application lives in a single codebase and deploys as one unit. Simpler to build and debug than microservices. looks like
A monolith puts everything, auth, users, products, orders, payments, into a single codebase that deploys as one unit. When you push code, the entire application updates at once.
MyApp/
src/
auth/
users/
products/
orders/
payments/
One build → One deployment → One running processServices talk to each other through direct function calls. No network involved. Simple.
What microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data. look like
Microservices split the application into small, independently deployed services. Each service owns its own database and communicates with others over HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. or a message queue.
auth-service/ → runs on port 3001
user-service/ → runs on port 3002
product-service/ → runs on port 3003
order-service/ → runs on port 3004
payment-service/ → runs on port 3005What was a function call in the monolithWhat is monolith?A software architecture where the entire application lives in a single codebase and deploys as one unit. Simpler to build and debug than microservices. becomes a network request in microservices, with all the complexity that implies.
Side-by-side comparison
| Aspect | Monolith | Microservices |
|---|---|---|
| Setup complexity | Low | High |
| Development speed | Faster early on | Slower initially |
| Deployment | All at once | Independent per service |
| Scaling | Scale everything | Scale only what needs it |
| Debugging | One place to look | Trace across services |
| Team fit | Small to medium | Large, multi-team |
| Testing | Run one app | Need multiple services running |
| Tech stack | Unified | Mix and match per service |
| Data | Shared database | Database per service |
Communication: the key difference
This is where the trade-off becomes concrete. In a monolithWhat is monolith?A software architecture where the entire application lives in a single codebase and deploys as one unit. Simpler to build and debug than microservices., calling another moduleWhat is module?A self-contained file of code with its own scope that explicitly exports values for other files to import, preventing name collisions. is just a function call. In microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data., it's a network request that can fail, time out, or return unexpected data.
// Monolith: simple, fast, reliable
async function createOrder(userId, items) {
const user = getUserById(userId); // function call
const total = calculateTotal(items); // function call
processPayment(user, total); // function call
sendConfirmation(user.email); // function call
}
// Microservices: flexible, but more failure surfaces
async function createOrder(userId, items) {
const user = await fetch(`http://user-service/users/${userId}`);
const total = await fetch('http://product-service/calculate', { items });
await fetch('http://payment-service/charge', { user, total });
await fetch('http://email-service/send', { email: user.email });
// Any of these can fail or be slow
}When to choose a monolithWhat is monolith?A software architecture where the entire application lives in a single codebase and deploys as one unit. Simpler to build and debug than microservices.
Start with a monolith when you have a small team (under 10 developers), an unproven product, a tight timeline, or when your domain is not yet well understood. Almost all successful products started here.
When microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data. make sense
Consider splitting services when you have large teams that step on each other's deployments, parts of the system with very different scaling needs (a video encoding job shouldn't compete with your login APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.), or when different features genuinely need different technology.
The typical migrationWhat is migration?A versioned script that changes your database structure (add a column, create a table) so every developer and server stays in sync. path
1. Build a modular monolith (clear internal boundaries)
2. Grow until you feel real pain - slow deploys, team conflicts, scaling bottlenecks
3. Extract one service (auth and payments are common first candidates)
4. Learn from that extraction
5. Repeat only where justifiedNetflix has over 500 services. They started as a DVD-by-mail monolithWhat is monolith?A software architecture where the entire application lives in a single codebase and deploys as one unit. Simpler to build and debug than microservices.. They did not redesign everything on day one.
Quick reference
| Signal | Recommendation |
|---|---|
| Team < 10, early product | Start with monolith |
| Team > 20, multiple squads | Evaluate microservices |
| Uniform traffic | Monolith fine |
| Spiky, uneven load by feature | Microservices help |
| Low DevOps maturity | Stay with monolith |
| Weekly releases | Monolith is fine |
| Multiple daily deploys | Microservices unlock independence |
// Example: Monolith vs Microservices
// MONOLITH APPROACH
// app.js, Everything in one application
const express = require('express');
const app = express();
// All services in one app
const authService = require('./services/auth');
const userService = require('./services/users');
const orderService = require('./services/orders');
app.post('/orders', async (req, res) => {
// Direct function calls (fast, simple)
const user = await userService.getUser(req.userId);
const order = await orderService.createOrder(req.body);
await authService.logActivity(user, 'order_created');
res.json(order);
});
app.listen(3000);
// ---
// MICROSERVICES APPROACH
// order-service/app.js
const express = require('express');
const axios = require('axios');
const app = express();
app.post('/orders', async (req, res) => {
try {
// HTTP calls to other services (slower, more complex)
const user = await axios.get(`http://user-service:3001/users/${req.userId}`);
const order = await createOrder(req.body);
await axios.post('http://auth-service:3002/activity', {
userId: user.id,
action: 'order_created'
});
res.json(order);
} catch (error) {
// Network failures, timeouts, etc.
console.error('Service call failed:', error);
res.status(500).json({ error: 'Service unavailable' });
}
});
app.listen(3003);
// user-service/app.js (separate application)
const express = require('express');
const app = express();
app.get('/users/:id', async (req, res) => {
const user = await database.users.findById(req.params.id);
res.json(user);
});
app.listen(3001);
// auth-service/app.js (separate application)
const express = require('express');
const app = express();
app.post('/activity', async (req, res) => {
await database.activity.insert(req.body);
res.json({ success: true });
});
app.listen(3002);
// docker-compose.yml (needed for microservices)
version: '3'
services:
user-service:
build: ./user-service
ports:
- "3001:3001"
auth-service:
build: ./auth-service
ports:
- "3002:3002"
order-service:
build: ./order-service
ports:
- "3003:3003"