The 10 AM Crash
Every day, lakhs of Indians open the IRCTC website or app right before the Tatkal booking window opens at 10 AM. For a few minutes, an enormous number of people are all doing the same handful of things at once: logging in, searching trains, checking seat availability, and rushing to pay before seats run out. This is exactly the kind of moment that exposes whether a software system was built well or badly, because the traffic is not evenly spread across the app — it slams into two or three specific features (search and booking) while other features, like updating your profile or viewing past bookings, see almost no extra load at all.
Here is the question this chapter answers: if you were the engineer responsible for keeping that system alive during the rush, would you rather have built one giant program that does everything, or many small, separately-running programs, each responsible for one job? The answer — and the reasoning behind it — is the entire subject of microservice architecture. This is not a niche detail. It is one of the most important design decisions a software team makes, and it is asked about constantly in computer science interviews and system-design discussions.
One Kitchen, One Cook vs. a Food Court
Before any formal definitions, picture two ways of feeding a hundred hungry students at a school fair.
Setup A: One large kitchen with one head cook who personally handles everything — taking orders, frying samosas, making tea, billing, and washing plates. If a hundred students show up wanting tea specifically, the cook is now hopelessly overloaded, even though nobody wants samosas right now. Worse, if the cook burns their hand on the frying pan, every single function stops — billing stops, tea stops, everything stops, because it all depended on one person.
Setup B: A food court with four separate counters — a tea counter, a snacks counter, a billing counter, and a cleaning crew — each with its own staff, its own equipment, and its own supply of raw material. If tea suddenly gets a rush of orders, you simply add two more people to the tea counter only. The snacks counter keeps running exactly as before. And if the tea counter's kettle breaks, students can still buy samosas and get billed; only tea is unavailable.
Setup A is a monolith. Setup B is a microservice architecture. Everything in the rest of this chapter is really just this food-court idea, made precise enough to build real software from.
What Exactly Is a Monolith?
A monolithic architecture is a software application built and shipped as a single, unified program. All of its features — user accounts, product catalog, payments, notifications, and so on — live in one codebase, run inside one process (or identical copies of that one process), and typically share a single database. When one part of the code calls another part, it is an ordinary in-memory function call, because everything lives inside the same running program.
Monoliths are not automatically "bad." For a small app, or a team of two or three developers, a monolith is often the right choice: it is simple to build, simple to test, and simple to deploy, because there is only one thing to deploy. Problems appear specifically when the app grows large and when different parts of it need to scale, change, or fail independently — which is exactly what happens to a booking platform during a Tatkal rush.
What Exactly Is a Microservice?
A microservice architecture splits one large application into a collection of small, independent services. Each service is responsible for exactly one business capability — for example, a User Service, a Catalog Service, a Payment Service, and a Notification Service. Four properties define a true microservice, and all four must be present:
- Independent deployability: you can update, redeploy, or restart the Payment Service without touching or restarting anything else.
- Independent scaling: you can run 40 copies of the Catalog Service while running only 5 copies of the Notification Service.
- Owns its own data: each service keeps its own database (or its own portion of a database), and no other service is allowed to reach in and read or modify that data directly.
- Communicates over the network: since services run as separate processes (often on separate machines), they talk to each other using network calls — almost always by sending HTTP requests to each other's APIs, usually carrying data formatted as JSON.
Notice what changed compared to the monolith: a function call inside one program became a network request between two programs. That single change is the root of almost everything else in this chapter — both the benefits and the new problems.
How Services Actually Talk to Each Other
Inside a monolith, when the order-processing code needs the user's details, it simply calls a function that lives right next to it in memory. The call is essentially instantaneous:
def process_order(user_id, item_id):
user = get_user(user_id) # direct in-memory function call
price = get_price(item_id) # direct in-memory function call
status = process_payment(user, price)
if status == "SUCCESS":
return "Order Confirmed"
return "Order Failed"
In a microservice architecture, the Order Service does not contain the code for users or payments at all — those live in separate programs, possibly on separate machines. So instead of calling a function, the Order Service sends an HTTP request across the network and waits for a response:
import requests
def process_order(user_id, item_id):
user = requests.get(f"http://user-service/api/users/{user_id}").json()
item = requests.get(f"http://catalog-service/api/items/{item_id}").json()
payment_response = requests.post(
"http://payment-service/api/pay",
json={"user_id": user_id, "amount": item["price"]}
)
if payment_response.json()["status"] == "SUCCESS":
return "Order Confirmed"
return "Order Failed"
Trace what happens for process_order(107, 55). First, the code sends GET http://user-service/api/users/107. The User Service looks up user 107 and replies with a JSON body such as {"user_id": 107, "name": "Aditi Sharma"}. Next, it sends GET http://catalog-service/api/items/55, and the Catalog Service replies with something like {"item_id": 55, "price": 899}. Finally, it sends POST http://payment-service/api/pay carrying {"user_id": 107, "amount": 899}, and the Payment Service replies with {"status": "SUCCESS"}, so the function returns "Order Confirmed".
Three separate network round trips happened where the monolith needed zero. That is the fundamental trade-off of microservices: you gain independence between services, but you pay for it with network communication, which is slower and less reliable than an in-memory call, and which can fail in new ways (timeouts, dropped connections) that a monolith never has to think about.
Doing the Scaling Arithmetic
Let's build a simplified, hypothetical model to see exactly why independent scaling saves money and machines — not by quoting real IRCTC infrastructure numbers (which are not public), but by working through the arithmetic that any engineering team would do.
Suppose our booking platform has four services: User, Booking (search + seat booking combined), Payment, and Notification. On a normal day, each service receives about 5,000 requests per second, and one server instance — whether it's a monolith replica or a single microservice — can handle 1,000 requests per second. The formula every engineer uses here is simple:
instances_needed = ceil(peak_requests_per_second / capacity_per_instance)
On a normal day: 5,000 / 1,000 = 5 instances for each of the four services.
Now the Tatkal window opens. Booking requests jump 8×, to 40,000 requests per second, while User, Payment, and Notification traffic barely moves, staying near 5,000 requests per second each (people log in and get notified at the same steady rate; it's specifically the booking action that spikes).
Monolith approach: because every module lives inside the same deployable unit, the entire application must be replicated together to give the Booking module enough capacity. To cover 40,000 requests per second, you need 40,000 / 1,000 = 40 full copies of the monolith — each copy carrying the User, Payment, and Notification code too, even though those parts didn't need the extra capacity at all. If each full-stack replica costs ₹40/hour to run (it's heavier, since it loads every module into memory), the bill is:
40 instances x Rs.40/hour = Rs.1,600/hour
Microservice approach: only the Booking Service needs to scale to 40 instances. User, Payment, and Notification stay at 5 instances each, exactly as before. A single-purpose service instance is lighter and costs less to run — say ₹10/hour:
Booking: 40 x Rs.10 = Rs.400/hour
User: 5 x Rs.10 = Rs. 50/hour
Payment: 5 x Rs.10 = Rs. 50/hour
Notification: 5 x Rs.10 = Rs. 50/hour
Total = Rs.550/hour
Same peak traffic, same reliability, but ₹550/hour instead of ₹1,600/hour — a saving of ₹1,050/hour, or roughly 66% less, during the rush. This is the arithmetic reason large-scale systems (ticket booking, digital payments, e-commerce) move toward microservices as they grow: you pay to scale only the part of the system that is actually under pressure.
Fault Isolation: Why One Broken Service Shouldn't Sink the Ship
Return to the food court. If the tea counter's kettle breaks, the snacks counter keeps selling samosas. This property is called fault isolation: a failure in one service should not automatically bring down services that don't depend on it. In our booking platform, if the Notification Service crashes (say it can't send SMS confirmations for a while), a well-designed system still lets people search trains, book seats, and pay — they just don't get an SMS immediately. In a monolith, the same bug — an unhandled error in the notification code — can crash the entire process, because everything shares one memory space and one running program. When the process dies, booking and payment die with it, even though their code was perfectly fine.
It's important to be precise here, though: microservices give you the opportunity for fault isolation, not a guarantee. If the Booking Service is written so that it directly calls the Notification Service and simply waits forever for a reply, then a slow or dead Notification Service can freeze the Booking Service too — a problem called a cascading failure. Real systems guard against this with techniques like timeouts and "circuit breakers" (giving up on a slow service after a short wait and proceeding anyway). The architecture makes isolation possible; the team still has to design for it.
Common Misconception #1: "Splitting code into folders makes it microservices"
A very common mistake is to think that organizing one program's code into neat folders — user/, catalog/, payment/ — makes it a microservice architecture. It does not. If all that code still gets built, deployed, and run as one single process sharing one database, it is still a monolith, just a tidier one. The defining test is not "how is the code organized on disk?" but "can I deploy, scale, and restart this one piece completely independently of the others, with its own database?" If the answer is no, folder structure alone doesn't change what it is.
Common Misconception #2: "Microservices are always the better choice"
Because this chapter has spent so much time on the benefits, it's tempting to conclude microservices are simply superior. They are not — they are a trade-off. Every network call we traced earlier (three round trips instead of zero) adds latency and new failure modes that a monolith never faces. Keeping four separate databases consistent with each other is genuinely harder than keeping one database consistent with itself — if the Payment Service confirms a payment but the network call telling the Booking Service to confirm the seat gets lost, you now have a paid-for seat that was never marked booked, a problem that simply cannot occur inside a single monolithic transaction. Running, monitoring, and deploying a dozen small services is also more operational work than running one program. For a small app, a school project, or a startup with three developers, a well-structured monolith is usually the smarter, faster choice; microservices earn their cost only once an application, its traffic, and its team have grown large enough that independent scaling and independent deployment start to matter more than the added complexity.
Where This Fits Into Your CS Journey
Microservices build directly on the client-server model you meet formally in CBSE Computer Science and Informatics Practices in Classes 11 and 12, where a client sends a request and a server sends back a response. A microservice architecture is simply many client-server relationships chained together — every service is a server to the ones that call it, and a client to the ones it calls. Understanding this now gives you a real head start: system-design thinking of exactly this kind shows up in computer science olympiads, in coding-plus-design interview rounds, and in any serious software engineering course you take after school. The core skill being tested is always the same one you practiced in the arithmetic above: given a system under load, can you correctly identify which part needs more capacity, and reason about the trade-off of splitting it out?
Quick Recall Check
- Which of the following is not a defining feature of a microservice? (a) it can be deployed independently (b) it owns its own data (c) it must be written in the same programming language as every other service (d) it communicates with other services over the network.
- True or False: inside a monolith, different modules typically communicate through direct in-memory function calls rather than network requests.
- A service receives a peak load of 12,000 requests per second. Each instance of the service can handle 800 requests per second. Using
instances_needed = ceil(peak / capacity), how many instances are required? - Short answer: why is organizing a single program's code into separate folders not the same as building a microservice architecture?
- Design task: a school library management system has four responsibilities — Book Catalog (search/list books), Member Records (student details), Issue/Return (lending logic), and Fine Calculation (late fees). Sketch how you would split this into microservices. Which two services would need to talk to each other when a student returns a book late, and what information would need to travel between them?
Answers: (1) c — microservices are commonly "polyglot," meaning different services can be written in different languages, since they only need to agree on the network API between them, not on internal code. (2) True. (3) 12,000 / 800 = 15 instances exactly. (4) Because the test of a true microservice is independent deployability, independent scaling, and an independently owned database — not folder layout. If all the folders still build into one process sharing one database, it remains a monolith. (5) When a book is returned late, the Issue/Return Service needs to tell the Fine Calculation Service the member's ID, the book's due date, and the return date, so Fine Calculation can compute and store the penalty — two independent services exchanging exactly the data needed for one job, and nothing more.
Summary
- A monolith is one application built, deployed, and run as a single unit, usually with one shared database and in-memory function calls between its internal modules.
- A microservice architecture splits an application into small, independently deployable services, each owning its own data and communicating with other services over the network, typically via HTTP APIs carrying JSON.
- The core arithmetic benefit is independent scaling: you only add capacity (and cost) to the specific service under load, instead of replicating the entire application.
- Fault isolation means a crash in one service need not take down unrelated services — but only if the team actively designs for it using techniques like timeouts and circuit breakers.
- The cost of microservices is real: extra network latency, harder data consistency across services, and more operational complexity than a single well-built monolith.
- Folder organization is not architecture. The test is independent deployability, independent scaling, and independent data ownership.