Lesson 1 of 5
Monolith vs Microservices
"Any organization that designs a system will produce a design whose structure is a copy of the organization's communication structure." — Melvin Conway
There is no more over-sold idea in modern software than microservices. Conference talks, vendor blogs, and résumés all whisper the same promise: break your app into dozens of tiny services and you will scale like Netflix. What they leave out is the bill. Splitting one program into many turns every method call into a network call, every transaction into a distributed problem, and every deploy into a choreography. This chapter is the honest ledger — what a monolith buys you, what microservices buy you, and the hard truth about when the second is worth its very real cost.
Should your system be one deployable unit or many independent services — and how do you tell the difference between a scaling problem microservices actually solve and one they just make more expensive?
Core Concepts
The monolith: one deployable unit
A monolith is a single application containing all your features — auth, billing, catalog, notifications — compiled and deployed as one unit, usually talking to one database. This is not a slur. It is how almost every successful company started, and how most should.
The strengths are exactly the things junior teams underrate. One codebase you can open in one editor and reason about end to end. In-process calls — no network, so no latency, no partial failure, and a single database transaction can wrap a change across billing and orders atomically. One thing to deploy, test, and debug: a stack trace runs from the HTTP handler to the SQL call without crossing a wire. For a small team and an early product, this simplicity is not a compromise — it is a superpower.
The weaknesses appear at scale, and they are largely organizational. When fifty engineers across six teams share one codebase, they contend over the same files, the same release train, the same deploy. You must ship everything together — a one-line fix waits for the whole app to be tested and released. You cannot scale the parts independently: if only the image-processing path is CPU-hungry, you still have to run more copies of the entire app to get more of it. One memory leak or infinite loop can take the whole thing down. And you are locked into one tech stack for everything.
Before you reach for microservices, reach for the middle ground:
• The modular monolith — one deployable unit, but with strict internal module boundaries: billing may only call orders through a defined interface, never its tables. You get clean seams and a single deploy.
• It keeps in-process speed and real transactions while making a future split cheap — the modules become the service boundaries.
• Most teams that "need microservices" actually needed a well-structured monolith. This is the most underrated architecture in the industry.
Microservices: many small, independent services
Microservices decompose the system into many small services, each independently deployable, each owning its own data, each communicating over the network — usually HTTP/gRPC for synchronous calls or a message broker for asynchronous events.
The strengths are real and specific. Teams deploy independently — the orders team ships ten times a day without asking anyone. You scale independently — run 40 copies of the image service and 3 of billing, sized to their load, not the app's. Each service can use the right tech — Go for the hot path, Python for the ML service. And you get fault isolation: if notifications crash, orders keep flowing (if you designed the boundaries well).
Now the honest bill — and it is heavy. You have just opted into distributed systems — every hard thing from Part V arrives at once. A local method call that never failed becomes a network call that adds latency and can time out, retry, or vanish. You lose cross-service transactions: a monolith wraps "charge card + reserve stock" in one commit; across services you need a saga — a sequence of local transactions with compensating undo steps, and eventual (not immediate) consistency. Operational overhead explodes: you now need centralized logging, distributed tracing, metrics, service discovery, health checks, and a deployment pipeline per service. Testing an end-to-end flow means standing up many services. And the hardest part of all — getting the boundaries right. Split wrong and every feature touches five services at once.
A monolith is a house where every room is down the hall — you walk to the kitchen in seconds and carry three plates at once without dropping any (one transaction). Microservices are a neighborhood of separate houses: each family owns and renovates theirs freely (independent deploy), but now dinner means driving between houses (network latency), the food might not arrive (partial failure), and there is no single moment where every dish lands on every table at once (eventual consistency). Separate houses are wonderful — once you actually have enough families to fill a neighborhood.
The comparison
| Dimension | Monolith | Microservices |
|---|---|---|
| Initial complexity | Low — one app, one repo, one deploy | High — distributed system from day one |
| Deployment | All at once; one pipeline | Per service; many pipelines, orchestration |
| Scaling | Whole app together (coarse) | Per service, independently (fine-grained) |
| Team fit | Small teams / one team | Many teams needing autonomy |
| Data & transactions | One DB, easy ACID transactions | DB-per-service; no distributed txn → sagas, eventual consistency |
| Failure isolation | Weak — one bug can down all | Strong (if boundaries are good) |
| Tech flexibility | One stack | Right tool per service |
| Operational cost | Low — one thing to run & watch | High — tracing, discovery, per-service ops |
| Debugging | One stack trace, end to end | Across service logs; hard |
| Best when | Early product, small team, unproven domain | Many teams, proven domain, org-scale pain |
The key lesson: start with a monolith
Say it plainly: start with a monolith. Microservices primarily solve an organizational scaling problem — many teams that can no longer share one codebase and one release train — far more than a technical one. Most technical scaling (more traffic, more data) is better solved first with caching, read replicas, a bigger box, and background workers, all of which a monolith supports fine.
This is where Conway's Law (our opening quote) earns its keep: your architecture will end up mirroring your org's communication structure whether you plan it or not. Microservices work at Amazon and Netflix because those companies have hundreds of small autonomous teams — the architecture matches the org. A ten-person startup has no such structure to mirror, so imposing microservices creates coordination overhead the org isn't built to absorb.
Amazon's early-2000s "two-pizza teams" and its famous internal mandate — every team exposes its data only through service interfaces, no shared databases — is the origin story microservices advocates cite. But the lesson usually gets truncated. Amazon adopted services because it had grown into thousands of engineers strangling on a shared codebase; the architecture solved an organizational crisis at massive scale. Netflix did the same after a 2008 database corruption took down DVD shipping for days. Both were escaping pain they had actually earned. Copying their solution without their problem is cargo-culting the endpoint of a decade-long journey.
chika.ng is a monolith: one Next.js application talking to one MySQL database through Drizzle, on a single host. Auth, the Academy, the Library, enrollment, and the admin dashboard all live in one deployable unit. This is exactly right for a solo/small operation — a change ships in one deploy, a bug shows one stack trace, and a certificate write plus an enrollment update happen in one transaction. Splitting this into an "auth service," a "library service," and a "billing service" would add network hops, distributed-transaction headaches, and a fleet of pipelines to babysit — pure overhead bought with money and complexity, in exchange for autonomy that a one-person team does not need. The right architecture is the one that matches your scale, not the one on the conference slide.
You split your app into eight microservices, but rendering the home page still requires calling all eight in sequence, they share concepts so a feature change touches five of them at once, and you can only deploy them together because they'd break otherwise. What have you built? A distributed monolith — the coupling of a monolith plus the network cost of microservices: the single worst outcome, and the most common one. The tell is simple: if you can't deploy one service without deploying the others, you never had microservices — you had a monolith with extra latency and a bigger AWS bill.
Common Mistakes
- Starting with microservices. Before product-market fit you don't yet know your domain boundaries — and boundaries are the one thing microservices demand you get right. Guess wrong early and every service split becomes a migration.
- Building a distributed monolith. Services that must be deployed together, or that reach into each other's databases, give you all the cost of distribution and none of the independence.
- Splitting by technical layer, not domain. A "database service," a "logic service," and a "UI service" means every feature crosses all three. Split by business capability, not by tier.
- Ignoring the operational tax. Microservices need tracing, centralized logs, service discovery, and per-service CI/CD before they pay off. Teams that skip this fly blind across a dozen processes.
- Confusing organizational scale with traffic scale. High traffic on a small team is a caching/replica problem, not a microservices problem. Microservices are for when the team can't share a codebase anymore.
- Cargo-culting Netflix. "It works for them" ignores that they have thousands of engineers and the platform teams to run it. Copy the reasoning, not the diagram.
Interview Corner
- Sketch the diagram: take an e-commerce app and draw it twice — once as a monolith (one box, one DB) and once as microservices (gateway + auth, catalog, orders, payments, each with its own DB). Mark every place a network call replaces an in-process call.
- Say the trade-off out loud (as in a micro1 screen): "I'd start this project as a monolith because ___, and I'd only split when ___." Fill both blanks in one breath.
- Estimate the cost: your monolith runs on 3 servers. You split into 6 services, each needing 2 instances for redundancy plus a message broker and a tracing stack. Roughly how many moving pieces must you now run and monitor — and who watches them at 3am?
- Pick one feature of your current project and describe how you'd extract it using the strangler-fig pattern, step by step, without a big-bang rewrite.
Chapter Summary
- A monolith is one deployable unit with one database — simple to build, test, deploy, and debug, with fast in-process calls and real transactions. Ideal for small teams and early products.
- Microservices are many independently deployable services owning their own data over the network — buying independent deploy/scale/tech and fault isolation at the cost of distributed-systems complexity.
- The costs are real and heavy: network latency and partial failure, no cross-service transactions (sagas + eventual consistency), and a large operational tax (tracing, discovery, per-service pipelines).
- Start with a monolith. Microservices solve organizational scaling (many teams) more than technical scaling; adopt them only when team/codebase pain justifies the cost.
- Conway's Law: architecture mirrors org structure — make that deliberate. The modular monolith is the underrated middle ground; the distributed monolith is the worst of both.
- If you split: by business domain / bounded context (DDD), not technical layer; database-per-service; migrate gradually with the strangler-fig pattern — never a big-bang rewrite.
Splitting into services forces one question to the surface: how do independent services talk without becoming tightly coupled again? Synchronous request/response chains recreate the distributed monolith — one slow service stalls them all. The escape is to let services communicate through events: fire-and-forget messages a service publishes without knowing or waiting on who consumes them. That's the subject of the next chapter — Event-Driven Architecture — where decoupling, asynchrony, and eventual consistency stop being costs to endure and become the design itself.
Editorial reflection — microservices are the clearest case in our field of an advanced solution mistaken for a best practice. They are not a rite of passage or a sign of seriousness; they are a specific, expensive answer to a specific, organizational problem most teams do not yet have. The engineers who impress me are not the ones who can wire up forty services — it's the ones who can look at a whining monolith, correctly diagnose whether the ache is technical or organizational, and have the confidence to say "not yet." Simplicity is not what you build before you know better. It is what you protect because you do.
Q&A · 0
Enrol to ask questions and join the discussion.
No questions yet — be the first to ask.