Module 0 · How software actually works

0.4 Monoliths, services, and how real products are structured

Lesson 0.4 · 10 min read

So far we've talked about a product as if the server were a single thing. One warehouse, one worker, taking your request and handing back an answer. That was a useful simplification to learn the journey. Now we take it apart, because the moment a product gets real, that single warehouse stops being enough, and how you split it up becomes one of the most consequential decisions in all of software.

This is the post where you stop seeing an app as a screen with some code behind it and start seeing it as a structured system with parts, boundaries, and deliberate organization. That shift in vision is what lets you work on serious applications instead of toys. A toy app is one room. A real product is a building, or a whole campus, and someone drew the floor plan on purpose. By the end of this post, you'll be able to read that floor plan, and eventually draw your own.


Start with the kitchen

Every real application has to do many different jobs.

Take a food delivery app. It has to manage user accounts and logins. It has to handle restaurants and their menus. It has to process orders. It has to take payments. It has to track the delivery in real time. It has to send notifications. Each of these is a genuinely different kind of work, with its own rules, its own data, its own tricky edge cases. Payments alone is a deep, high-stakes world. So is live location tracking. So is search.

Here's the central question this whole post answers: do you build all of that as one giant program, or as a set of smaller separate programs that talk to each other?

Picture a restaurant kitchen. One option is a single enormous station where one team does absolutely everything: they take the order, cook the appetizer, grill the main, plate the dessert, wash the dishes, and handle the bill, all crammed into one space, all tangled together. The other option is stations: a grill station, a salad station, a dessert station, a dish pit, each with its own team, its own tools, its own space, passing plates between them through a clear system. Both kitchens serve the same meal. But they are organized in fundamentally different ways, and that organization shapes everything about how they work, how they fail, and how they grow.

That's the choice between a monolith and microservices. Let's take each seriously.


The monolith: everything in one place

A monolith is an application built as a single unified program. All the features, accounts, restaurants, orders, payments, notifications, live in one codebase, run together as one unit, and are deployed all at once. It's the single giant kitchen station. Everything in one place, tightly connected.

The word "monolith" gets said with a sneer sometimes, as if it's the naive beginner choice. That's wrong, and believing it will make you a worse engineer, not a better one. A monolith has real, serious advantages, especially early. Because everything lives together, the parts can talk to each other easily and instantly, no network trips between them, no complicated coordination. There's one thing to build, one thing to deploy, one thing to understand. For a small team building a new product, this simplicity is a genuine superpower. You move fast because there's less machinery in your way. A huge number of successful, large products are monoliths, and some stay that way deliberately for years. Starting with a monolith is very often the correct, disciplined engineering decision, not a compromise.

The trouble with a monolith shows up later, as it grows large and the team grows with it. When everything is tangled together in one enormous codebase, a few specific pains set in. A change in one corner can accidentally break something in a completely unrelated corner, because it's all connected. The whole thing has to be deployed together, so a tiny fix to notifications means redeploying the entire massive application, payments and all. Dozens of engineers working in the same giant codebase start stepping on each other. And you can't scale just the busy part; if search is under heavy load but payments is quiet, you still have to scale the entire monolith as one lump, because it is one lump. None of these are fatal. But past a certain size, they become a real drag, and that drag is what pushes teams toward the other approach.


Microservices: many small specialists

Microservices are the other answer. Instead of one big program, you build many small, independent programs, each responsible for one job, each talking to the others over the network. One service handles accounts. A separate service handles payments. Another handles notifications. Another handles delivery tracking. Each is its own little application, with its own code and often its own database, running on its own, communicating through requests, exactly the kind of request-and-response we learned in the first post, but now happening between the services themselves.

This is the kitchen with stations. And you can immediately feel the appeal, because it fixes the monolith's late-stage pains almost point for point. Each service can be built, changed, and deployed on its own, so a fix to notifications never touches payments. A small team can fully own one service without wading through everyone else's code. And you can scale precisely: if search is slammed, you add power to just the search service and leave the rest alone. For very large products with very large teams, this independence is enormously valuable, which is why nearly every giant tech company runs on microservices at scale.

But, and this is the part that beginner takes on the subject always skip, microservices are not free. They trade one kind of difficulty for another, and the new difficulty is often harder. The moment your services talk over the network, you inherit every problem of the network: latency between every internal call, the possibility that one service is down while others are up, the challenge of a single user action that now has to coordinate across five separate services without ending up in a broken half-finished state. Testing gets harder. Understanding the whole system gets harder, because there is no single place where it all lives; the logic is spread across many services and the connections between them. You've turned one complicated program into many simple programs plus a complicated web of relationships, and that web is its own beast. Splitting a kitchen into stations only helps if the hand-offs between stations are smooth. If plates get lost passing between them, you've made things worse, not better.

Interactive: monolith vs microservices

A hands-on visual for this idea is in the works. The text around it carries everything you need for now.


The honest truth about which is "better"

Here's where a lot of writing on this topic does you a disservice, by making it sound like microservices are the modern, correct answer and monoliths are the outdated one. The reality is more useful and more interesting than that.

Neither is better. They're different tools for different situations, and the entire skill is matching the choice to the moment. A small team building a new product almost always benefits from a monolith: the simplicity lets them move fast and figure out what they're even building, which is what actually matters early. A massive organization with hundreds of engineers and a product under enormous load almost always benefits from microservices: the independence is worth the coordination cost at that scale. Most of the interesting engineering happens in the enormous middle, where teams start as a monolith and, as specific parts start to hurt, carefully break those parts out into services, one at a time, only where the pain justifies it. This gradual, deliberate path is how many of the systems you use every day actually evolved. They didn't choose one religion at the start. They responded to real pressure as it appeared.

So the mature question is never "monolith or microservices?" as a matter of fashion. It's "what does this product, at this size, with this team, actually need?" That's an engineering judgment, and being able to reason about it, to weigh simplicity against independence for a specific situation, is exactly the kind of thinking that separates someone who can build a real system from someone who just follows whatever sounds most current. When you build with AI and it offers to architect something for you, this is a decision you want to understand and direct, not one you want to accept blindly, because getting it wrong in either direction is expensive.


How to actually read a system

Let's make this practical, because you'll encounter real systems and need to make sense of them. Whether a product is a monolith or a set of services, engineers describe its structure with diagrams: boxes connected by lines. Once you understand what you're looking at, these diagrams stop being intimidating and become genuinely readable, even the complex ones.

Here's the whole secret to reading them. The boxes are the parts that do work: a service, a database, the client app, an outside system like a payment provider. The lines are the conversations between them, the requests and responses flowing back and forth, the exact journeys we've been studying all along. That's it. A system diagram is just a map of who talks to whom. When you trace a single user action along the lines, follow it from the client, to whichever service catches it, to the database it reads from, to the other services it has to notify, you are reading the architecture. You're seeing the real shape of the thing.

And once you can trace those lines, you can see things that matter. You can spot where a system might get slow, because you can count the hops a request has to make. You can spot fragility, a place where one box, if it fails, would take down many others that depend on it. You can spot where data lives and where it has to travel to. This is the skill we pointed at earlier in the module, now made concrete: looking at how a product is structured and actually understanding its behavior, its costs, and its weak points, from the map alone. It's one of the most quietly powerful things a design engineer can do, and hardly anyone teaches it deliberately.


Where this leaves you at the end of Module 0

Step back and see what you're now holding, because you've built something real across these four posts.

You understand that every app is a conversation between a client and a server, a journey of requests and responses that you can follow step by step. You understand that content is either static, made once and the same for all, or dynamic, built fresh per person from live data, and that this line predicts most of a product's difficulty. You understand that all of this happens across real physical distance, at a real cost in time, and that this cost is something you design for, not something you hope away. And now you understand that the server itself isn't one thing but a structured system of parts, organized deliberately as a monolith or as services, and that you can read that structure from a diagram and reason about how it will behave.

That is the mental foundation. It's genuinely load-bearing. Everything from here, the frontend, the APIs, the databases, the scaling, the AI infrastructure, is a closer, deeper look at one part of the picture you now hold in your head as a whole. You didn't get the tourist version. You got the real shape of how software works, which is exactly what you need to start building things that are real.


Do this before the next module

Pick a product you know well and use often, and try to sketch its structure, badly, on purpose. You don't need it to be correct. You need to attempt it, because the attempt is where the learning happens.

Draw a box for the app on your phone. Draw a box for a server. Now ask yourself what different jobs this product does, accounts, search, messaging, payments, whatever fits, and draw a box for each, imagining them as separate services. Draw a box for the database. Then draw lines: when you open the app and it loads your home screen, which boxes have to talk to each other, in what order? Trace that one action across your sketch.

It will be rough and probably wrong in places, and that's completely fine. What you're doing is forcing yourself to see a familiar product as a structured system for the first time, instead of a flat surface. Do that once, by hand, and something clicks that no amount of reading can give you. You've started thinking like someone who builds these things, not just someone who uses them. That's the whole point of Module 0, and you've arrived.