Module 1 · Lesson 2 of 5
The design process
Requirements, high-level design and low-level design, followed through one feature.
"Build an app for the fest" is not something you can design. It's a wish. This lesson gives you a process that turns a wish into a design in three passes, each one sharper than the last, and you'll follow one Gatepass feature all the way through.
Three passes, coarse to fine
Good designers don't start with database columns, and they don't stop at boxes and arrows. They zoom in gradually:
- Requirements. What must the system do, for whom, and how well? Out come a list of features and a handful of numbers.
- High-level design. Which big parts exist and how does data move between them? Out comes a diagram you could explain in two minutes.
- Low-level design. For the parts that matter, what exactly are the API endpoints, the tables, the algorithms? Out come things a developer could build from.
The percentages are a rough guide for a design session, not a rule. The point is that requirements are short but come first, and that most of the real thinking happens once you get specific.
Pass one: requirements
Requirements split into two kinds, and the next lesson covers them in depth. For now:
- Functional requirements are verbs: list events, buy a pass, scan a pass at the gate.
- Non-functional requirements are adjectives with numbers: fast (pages under 300 ms), available (up all of fest week), correct (never oversell).
The most useful thing you can do in this pass is say no. Gatepass on day zero does not need refunds, seat maps, a waitlist or a friend-invite feature. Writing them down as "out of scope" is a decision, and it protects every later pass from creeping complexity.
Pass two: high-level design
Now draw the big parts. For Gatepass, a sensible first sketch is:
At this level you're deciding things like: one server or many? Do clients talk to the payment provider directly or through us? Does the gate scanner need to work offline? You're not yet naming columns.
A good high-level design lets you trace each functional requirement through the boxes. "Buy a pass" goes app → API → payment provider → API → database → app. If a requirement can't be traced, a box is missing.
Pass three: low-level design
Pick the parts that carry the risk and get specific. For Gatepass, the risky part is buying a pass, so that's where the detail goes:
- API:
POST /events/{id}/bookingswith the pass type and a payment token. Returns201with the booking, or409if sold out. - Schema: a
passestable with aremainingcount, and abookingstable that references it. - Algorithm: decrement
remainingonly if it's above zero, in the same transaction that inserts the booking. If the decrement touches zero rows, the event is sold out.
That last line is the whole correctness story in one sentence. It's the kind of detail an interviewer or a reviewer wants to hear, and it's invisible in the high-level diagram.
UPDATE passes SET remaining = remaining - 1
WHERE id = $1 AND remaining > 0;
-- 0 rows updated means sold out: roll back and return 409
Following one feature through
Let's run "scan a pass at the gate" through all three passes, quickly.
Requirements. A volunteer scans a QR code; the app says valid or already used within a second. The college Wi-Fi at the gate is famously bad.
High-level. That Wi-Fi detail changes the design. If every scan needs the server, a dead connection stops the queue. So the scanner app downloads the list of valid pass IDs before the gates open and checks locally, then syncs scans to the server when it can.
Low-level. The QR code holds the pass ID plus a signature so it can't be forged. The scanner keeps a local set of used IDs. With two gates, the same pass could be scanned at both before they sync, so you accept that risk (it's a college fest, not a bank) or give each gate its own range of passes.
Notice how a requirement nobody would have put in a diagram (bad Wi-Fi) reshaped the whole feature. That's why requirements come first.
Your turn
Next year the committee wants refunds up to 48 hours before the fest. Name the decisions this adds in each pass.
Stuck? Think about money going back through the payment provider, and the pass becoming sellable again.
When to loop back
The process is drawn as a line, but you'll walk it like a loop. Low-level detail often breaks a high-level choice: you discover the payment provider calls you back with a webhook, so you need an endpoint for it and a way to match it to a booking. Fine. Go back, add it, carry on.
What you should avoid is the opposite: jumping into low-level detail before the requirements exist. That's how teams end up with a beautiful sharded database for an app with 200 users.
Key takeaways
- Design in three passes: requirements, then high-level, then low-level.
- Requirements come first because one detail (bad Wi-Fi at the gate) can reshape a feature.
- High-level design should let you trace every functional requirement through the boxes.
- Spend low-level detail where the risk is. For Gatepass that's never overselling.
- Loop back freely; skipping ahead to detail is the mistake.
Finished reading?
Mark it complete to tick it off in the course.