Session 3 taught you what a backend is. Session 4 is about what happens when the backend is under real pressure — when 12,000 people hit “Buy Pass” in the first minute and your design either holds or melts.
First we learn to think in systems. Then we use it right away to design a Navratri pass booking system together — from the first vague idea to the last hard choice.
Tonight's run sheet
Thinking in systems
Designing the system
Design sprint
Part 1 of 3 · 4:15 – 4:30
Thinking in systems
Features vs systems, what is ours vs theirs, good loops and bad loops — and why quick fixes fail.
01. Features Are Not Systems
Most beginners build a system by listing features: login page, pass list, checkout, payment, email confirmation. Each feature feels like progress. But a feature is a local improvement — a system is the whole thing plus the interactions between its parts.
The trap: optimising one part can make the entire thing worse.
A useful test when you are about to change something: what else does this change affect?
- One “Buy pass” button touches seats, payments, messages, reports, refunds, support replies, and database speed. Change anything about buying, and all of them feel it.
- A “small” change like allowing 10 passes per order makes your database save 10× as much at rush hour — even though on screen it is one checkbox.
The three questions of systems thinking
What are the parts?
How do they interact?
What state do they hold?
02. Feedback Loops and Delays
A feedback loop is when what comes out of a system loops back and changes what goes in. Think of a microphone held too close to a speaker: a tiny sound comes out, goes back in louder, comes out louder still — until the whole hall is screeching.
Loops come in two flavours. A bad loop feeds itself — every round makes the next round worse. A good loop fixes itself — every round calms the next one down. Most launch-day disasters are a bad loop nobody drew on a whiteboard beforehand.
Four ways to break a bad loop:
- Make the app wait before retrying — and wait a random amount, so 10,000 phones do not all knock again in the same second.
- A waiting line with a number — take everyone’s click, keep it safely on your side, and serve people a few at a time. The site stays fast even when it cannot serve everyone at once.
- Safe retry codes — tapping “pay” twice by accident must book once, not twice. Stamp every attempt with a unique code; when the same code arrives again, return the first answer instead of charging again.
- An automatic cut-off — if the payment company stops answering, stop calling it for a while so the rest of the site stays alive.
Part 2 of 3 · 4:30 – 5:25
Designing the system
What to build, how buyers move, where data lives, logins, the 9 AM rush, and staying online.
03. The Problem: A Navratri Pass Booking System
Nine nights of dandiya, one venue, and a committee that wants every pass sold — without selling a single pass twice.
Numbers first, screens later
Asking for requirements just means turning a vague wish (“sell passes online”) into numbers you can argue about. For tonight, let us fix these numbers:
Peak load
What we sell
What we cannot lose
| What we promise | What kind | Why it decides the design |
|---|---|---|
| A pass is confirmed within 10 seconds | Speed promise | This one line decides how patient every part must be: how long a payment may take, how long a buyer may wait in line |
| Never sell more passes than exist | Must never break | The one mistake that destroys trust on day one |
| The “seats left” number is right, give or take one | Must be true | Decides how often we ask the database instead of showing a saved copy |
| Survive 2,000 clicks a second for 6 hours | Strength promise | Decides how many machines we rent and what it costs |
| Keep working when the payment company goes down | Backup-plan promise | Booking must slow down, never die |
| Works on a phone with patchy 4G | Hard limit | Half the crowd is on mobile data, so pages must be light |
Requirements are not the features you build. They are the lines you must stay inside. And a line you cannot measure — “fast”, “reliable” — is not a requirement, it is a wish.
04. System Boundaries
A system boundary answers one question: what is our job to fix, and what belongs to somebody else?
Notice what sits outside the dashed line: the payment company, the SMS company, the students’ phones. You cannot fix any of those. You can only build your part so it survives their bad days — that is the real meaning of thinking in systems.
05. The Buyer’s Journey
A flow is not a drawing of screens. It is the list of choices a buyer makes, in order: look, pick, pay, done. Get the choices right first — the screens almost design themselves afterwards.
Four choices hiding in that picture:
- Reserve first, pay second. We lock the seat and take money after — because money taken for a seat that does not exist has to be returned, with an apology.
- Every lock has a timer. A lock with no timer is a seat lost forever. Every held seat counts down and frees itself.
- Confirm fast, inform slowly. The buyer sees their QR in 2 seconds; the email can stroll in 30 seconds later. Never make the buyer wait for another company’s server.
- Say what went wrong, out loud. A failed payment must tell the buyer what happened and what to press next. Silence creates 500 “where is my pass?” messages.
06. Write the Tables First
Before a single button or screen, write down your tables. The tables are where your rules actually live — everything else just reads them.
CREATE TABLE slots (
id SERIAL PRIMARY KEY,
tier TEXT NOT NULL, -- 4 tiers: 'regular' | 'premium' | 'vip' | 'vvip'
capacity INT NOT NULL,
held INT NOT NULL DEFAULT 0, -- seats people are holding right now
sold INT NOT NULL DEFAULT 0, -- seats fully paid for
-- the database itself says no to seat number 5,001
CHECK (held + sold <= capacity)
);
CREATE TABLE passes (
id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES pass_orders(id),
slot_id INT NOT NULL REFERENCES slots(id),
qr_token TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Three choices worth arguing for:
CHECK (held + sold <= capacity)— the cheapest, strongest promise you can make. Even if our code has a bug, the database says no to an impossible state. Put the rules where nobody can walk around them.- One row per pass, not a “how many” number on the order. Every pass gets its own QR code. That means checking a pass at the gate is one quick look-up, and a lost pass can be refunded without touching the rest of the order.
- One order can have only one payment row. So “was I charged twice?” always has a yes-or-no answer when we match money to passes at the end of the day.
07. Who Are You? Are You Allowed?
Logging in and being allowed are two different questions, and mixing them up is how apps show people other people’s data. Logging in asks “who are you?”. Permission asks “are you allowed to see this?”. Ask both, on every request.
| Role | Can do | Cannot do |
|---|---|---|
| Visitor | Look at tiers and seats left | Hold or buy anything |
| Buyer | Hold and buy passes, see only their own, join the waiting list | See anyone else’s passes |
| Organiser | Add seats, see sales, download the list | Edit a payment (payments are write-once history) |
| Support engineer | Read the health graphs, refund a pass | Approve their own big refund |
Rules we never break:
- Always ask: whose data is this? Every time we fetch passes or orders, we only show the logged-in person their own stuff. Never trust the pass number in the web address alone — check that it belongs to them.
- Payment history is write-once. To refund, we add a new refund row; we never edit or delete the old row. When money is disputed, that history is the proof.
- Write down every admin change. Who added seats to which tier, when, and how many. If seats can be added by hand, someone will one day add them wrong.
- Put a speed limit on repeat requests. The code that sends SMS codes especially — without a limit, one person can burn thousands of rupees of text-message credit in an hour.
08. The Hard Part: Two Buyers, One Seat
This is the problem that separates a college project from a system you can trust with real money.
This code looks correct. It is not:
// ❌ This is how 5,001 people end up holding the 5,000th pass.
const slot = await db.query("SELECT held, sold FROM slots WHERE id = ?", [
slotId,
]);
if (slot.held + slot.sold < slot.capacity) {
await db.query("UPDATE slots SET held = held + 1 WHERE id = ?", [slotId]);
return createHold(orderId);
}
return { error: "Sold out" };
Between the “look” and the “write”, another buyer slips in. Both buyers read “4,999 taken”. Both see one seat free. Both take it. Now 5,001 people are holding the 5,000th pass.
// ✅ One single step: the database picks the winner.
const result = await db.query(
`UPDATE slots
SET held = held + $2
WHERE id = $1
AND held + sold + $2 <= capacity
RETURNING id, capacity, held, sold`,
[slotId, quantity],
);
if (result.rowCount === 0) {
return { error: "sold_out" }; // → waitlist, not an error page
}
// the seat and the hold are written together — all or nothing
Three more tricks the industry uses — same problem, different tools:
Lock the seat, then think
Assume nobody else is buying
Make "try again" safe
09. Maths Before the Rush
Doing this maths before the rush means at 9:00 AM you are watching calm graphs instead of guessing.
Start from the number the buyer feels. About 12,000 people try to buy in the first minute, which is 200 real booking attempts a second. The 2,000 clicks a second include refreshes and browsing — roughly one click in ten is a real attempt. So the system must finish 200 bookings a second, end to end, or make the rest wait in line. Everything else is just splitting those 200 across the machines.
Where do the 10 seconds go?
Try it with our numbers:
When the crowd gets bigger
What helps you grow — and what each trick costs:
| Trick | What it buys | What it costs |
|---|---|---|
| Add more booking servers | Handles more buyers, almost in a straight line | Nothing much — this is the cheap one |
| Save the “seats left” number for 15 seconds | Takes ~90% of look-ups off the database | The shown number can be slightly old |
| A copy of the database just for looking | Reports and browsing don’t touch the real one | The copy is always a little behind — never trust it for something you just changed |
| Send email and SMS from a waiting line | A slow SMS company can’t block a booking | A message can occasionally arrive twice |
| Rent a bigger database machine | Simple, instant breathing room | Money — and you’ll outgrow this one too |
| Split seats into separate tables per tier | Spreads the writing across tables | Counting across tiers gets hard, and now you look after two designs instead of one |
10. When Things Break
You can’t fix what you can’t see — so build the windows before you need them.
The signals that matter
Booking time for 95 out of 100 buyers
Buyers arriving per second
What exactly is failing
How full the database is
Passes sold per minute
Money in vs passes out
Breakages we plan for on purpose:
| Breakage | What the buyer sees | What we do instead |
|---|---|---|
| Payment company stops answering | “Sorry — we could not confirm your payment” | Try charging once more with the same safe-retry code; if it still fails, free the seat and say so |
| Database is full | Sorry page | Turn people away politely at the front door, and tell them when to try again |
| Email-sender crashes mid-email | Buyer sees nothing wrong | The waiting line tries again later; the pass already exists, the email is just late — and trying again must never charge twice |
| Fast memory is down | Counts may be old or missing | Ask the database directly. Slower, but right. And never trust a saved count so much that losing it sells a seat twice |
| Updating the site at the worst moment | Error pages for 30 seconds | Update when the crowd is asleep, and always be able to undo it in under a minute |
Writing the “what happened” note
After a bad day, explain in one page what happened and why — four questions, no blame:
- Impact — how many buyers, how much money, for how long?
- Timeline — what happened first, and what did we believe at the time?
- Real cause — what made the failure possible in the first place, not what broke last.
- What we change — one way to notice sooner, one way to prevent, one backup plan. No blame, and specific.
Part 3 of 3 · 5:25 – 6:00
Design sprint
Choices out in the open, then teams attack their own design for 30 minutes — and at least one team presents.
11. Every Choice Has a Price
There is no single “right” design here — only choices you can defend out loud. Here is the honest grid:
Decisions we are recording
This is how working teams write down decisions — short, dated, and kept forever, because six weeks from now nobody will remember why.
One single step claims a seat
Email and SMS wait in line
"Seats left" is saved for 15 seconds
A waiting line from 9:00 AM
SMS codes instead of passwords
Seat numbers are frozen before launch
12. Your Turn: The Design Sprint
Thirty minutes. Groups of three or four, one laptop per group, one shared document. After that, at least one team presents — more if time allows.
Write it down before you build it
- Three buyer stories ("as a buyer, I want…"), each with a way to check it is done
- Five promises about speed and strength, each with a number
- Two things you are explicitly not building
- One assumption you cannot verify before launch day
Three diagrams on one page
- Context: who and what is outside your boundary
- Inside: your boxes and the arrows between them
- The moment a seat is claimed, showing exactly where double-selling becomes impossible
Attack your own design
- What is the slowest part, and what number proves it?
- What breaks first: payments, database, or your code?
- Write down one decision: what you knew, what you chose, what it costs
At least one team takes the board
- Present the problem, the design, and the one decision you recorded
- Say what you gave up that worries you most
- Answer one question from the room
A question that actually helps
13. How to Keep Thinking Like This
Systems thinking isn’t a chapter you finish. It’s one habit made of three smaller ones:
- Draw before you build. A diagram you’re happy to throw away costs nothing. The design you fell in love with costs a week.
- Find the slowest part. In every system, one part sets the speed for everything. Find it before you improve anything else — and admit it when it’s something money can’t fix.
- Decide, then write it down. Every design is a list of things you gave up. If you can’t say what you gave up, you didn’t choose — you drifted.
“The best engineers are not the ones who know the most. They are the ones who can see the whole system — and know which part to change first.”
Key Takeaway
Features make a demo work. Systems thinking makes it survive launch day. Promises without numbers, rules nobody enforces, and maths you skipped are just hopes. Start from the slowest part, protect the one rule that matters, and write down what you gave up — that is how a project becomes engineering.