Glossary

Monolith

A monolith is an application where all components (UI, business logic, data access) run in a single process and are deployed as one unit. Most successful applications start as well-structured monoliths.

Explanation

All features — authentication, users, products, orders, payments — live in one codebase and deploy together. Function calls between components are in-process (nanoseconds), not network calls (milliseconds). One codebase to search, one deployment pipeline, one set of logs, one database. Monoliths are often depicted negatively, but unfairly. A well-structured monolith is simpler to develop, test, and operate than microservices. Type systems validate cross-module calls at compile time. Transactions spanning multiple domains are trivial. Debugging is straightforward — everything is in one process. The problem people actually fear is the "big ball of mud" — a monolith with no module boundaries where every change risks breaking something unrelated. This is a design failure, not an inherent property of monoliths. A modular monolith enforces clear domain boundaries (like microservices) while keeping the simplicity of a single deployment. Shopify, Stack Overflow, Basecamp, and GitHub all ran significant monoliths at scale before selectively extracting services. The advice "don't start with microservices" is near-universal among senior engineers who have operated both.

Code Example

javascript
// Modular monolith: domain boundaries in directory structure
// src/
//   modules/
//     users/
//       users.routes.js   — HTTP handlers
//       users.service.js  — business logic (the boundary)
//       users.repo.js     — database queries
//     orders/
//       orders.routes.js
//       orders.service.js
//       orders.repo.js
//   app.js               — wires modules together

// Cross-module: call via service interface only
const { getUser } = require('../users/users.service');

async function createOrder(userId, items) {
  const user = await getUser(userId); // service interface
  // NOT: const user = await db.query('SELECT * FROM users...') // bypasses boundary
}

// When to extract to a microservice:
// 1. A team needs to deploy independently without coordinating
// 2. One component needs dramatically different scaling (100x more requests)
// 3. You need a different technology stack for that domain
// 4. Domain boundaries are stable and well-understood

// NOT because microservices sound modern.

Why It Matters for Engineers

The monolith vs. microservices debate is a system design interview staple and a real architectural decision. Reflexively choosing microservices creates unnecessary complexity for teams that don't need it. Knowing when a structured monolith is correct — and being able to articulate why — demonstrates architectural judgment over fashion. Understanding monolith strengths also prepares you to recognize when microservice extraction is actually warranted: team autonomy blocked by coordination overhead, dramatically different scaling requirements, or genuine domain stability.

Related Terms

Microservices · API · Database Sharding

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Systems Design Fundamentals → →