Module 1 / Thinking in systems

Module 1 · lesson 1 of 5 · Read 7 min

Decisions, not diagrams

What you are really producing when you design a system.

Give two teams the same one-line feature: "Tell customers when their food is five minutes away."

Team A has the customer's phone ask the server for the rider's position every 30 seconds, and the phone works out the distance. Team B has the server watch every rider, estimate arrival times itself, and send a push notification when an order crosses the five-minute mark.

Ask both teams for a diagram and you get nearly the same picture:

Phones
→
Server
→
Database
Maps service
Team A's diagram and Team B's diagram: the same boxes.

Yet the two systems behave very differently. Team A drains batteries and goes quiet the moment the customer closes the app. Team B keeps working with the app closed, but its server now does far more work at dinner time. The diagram can't tell these designs apart. The decisions can.

A design is a set of decisions

A system design is the set of decisions that fix where data lives, how the parts talk to each other, and what happens when the system is busy or something breaks.

Most of those decisions fall into four families. They make a useful checklist for any feature, big or small:

Family The questions Team A vs Team B
Data What is stored, where, and which copy is the truth? Both store rider positions on the server. Only B stores ETAs.
Communication Who starts the conversation? Request and wait, or fire and forget? A: phone pulls. B: server pushes.
Failure What happens when a part is slow, down or wrong? A: nothing happens if the app is closed. B: a missed push means a missed alert.
Growth What breaks first at ten times the traffic? A: request volume from phones. B: ETA computation on the server.

Notice that none of these questions mention a product name. Choosing a specific product, such as the database Postgres or the in-memory store Redis, comes later, as a consequence of a decision ("rider positions change every few seconds and only the latest one matters"), never as the decision itself.

Decisions hide inside small features

Big systems get the attention, but most real design work happens inside small features. Take Tiffin's planned Reorder button: one tap repeats your last order.

It sounds like one line of code. Look closer:

  • The restaurant removed one of the dishes last week. Reorder the rest, or stop and ask?
  • The price went up by ₹20. Charge the new price silently, or show it first?
  • The restaurant is closed right now. Hide the button, or let people schedule?
  • Your saved address was deleted. Which address do we use?

Every one of these is a design decision with a data side (we need a menu version or a price to compare against), a communication side (does the app ask the server before showing the button?) and a failure side (what if the menu service is slow when you tap?). Skipping them doesn't avoid the decisions. It just means they get made by accident, usually by whoever writes the code at 11pm.

Find the hidden decisions

Pick one small feature from an app you use: Google Maps' "share my location for 1 hour", or WhatsApp's "delete for everyone". List at least one decision in each of the four families.

Show one answer for "delete for everyone"

Data: is the message actually erased from the server and every phone, or replaced with a "deleted" marker? Do backups still hold it?

Communication: the sender's phone tells the server, the server tells every recipient's phone. What if a recipient is offline for a week?

Failure: a recipient's phone never gets the delete. Does the sender see a warning, or does the system retry forever?

Growth: deleting from a group of 1,000 people means 1,000 deliveries. Is there a time limit (WhatsApp has one) partly to bound this work?

Constraints pick the winner

Back to the two teams. Which design is better?

It depends on things outside the feature:

  • Users. For a hackathon demo with 20 users, Team A ships in an afternoon and is the right call. For 5 million customers, push notifications that work with the app closed are worth the extra server work.
  • Team. Team B needs someone who can run a background service that watches every rider. If nobody on the team has done that, it's a risk, not a feature.
  • Time and money. A launch in three weeks rules out a lot of good ideas.
  • What already exists. If Tiffin already sends push notifications for "order accepted", Team B's design reuses that. Existing systems are constraints too.

So a good design is not the most capable one. It's one that meets the requirements within the constraints, and carries no more complexity than that requires. Every extra part you add has to earn its place in money, operational effort and new ways to fail. Adding parts for problems you don't have yet is overengineering, and it is the most common way well-meaning engineers make a system worse.

Write the decisions down

Because a design is decisions, the most useful design artefact is not the diagram. It's a short written record of each decision. Many teams call these decision records. A good one fits on half a page:

Decision:   Where do we compute "5 minutes away" alerts?
Options:    (a) on the customer's phone  (b) on the server
Chosen:     (b) on the server
Because:    Customers close the app while waiting. Alerts must work anyway.
Gave up:    ~40 extra ETA calculations/second at dinner peak on our servers.
Revisit if: server cost for ETAs becomes significant, or OS push limits bite.

The last two lines matter most. "Gave up" forces you to admit the cost. "Revisit if" tells a future engineer when the decision stops being right, so they can change it without guessing what you were thinking.

Meet Tiffin

Through this course we'll design one product end to end. Tiffin is a food delivery app: customers browse restaurants and order, kitchens see orders on a tablet, riders pick up and deliver, and payments flow between everyone.

Customer app
Kitchen tablet
Rider app
→
Serverone app
→
Databaseone Postgres
Tiffin on launch day. Three kinds of user, one server, one database.

It starts tiny: one city, one server, one database. Each module introduces a real problem that pushes Tiffin to grow, from slow menus to a database that can't keep up to customers in many cities. Each time, we'll add only the parts the problem asks for, and write down what we gave up.

Lessons work their numbers out from where Tiffin is at that point in the course. You don't need these yet, so they're folded away; come back whenever a lesson quotes one.

Tiffin's numbers at each stage of the course
Stage Modules Cities Orders a day Restaurants Riders at peak Rider location pings
Launch year 1-2 1 5k → 50k 300 → 800 1,000 Every 10 s
Year two 3-8 1 200k 3,000 6,000 10 s → 4 s
Year three 9-11 1 → 10 250k → 400k 15,000 10k → 15k Every 4 s
National 12-24 40, then 2 abroad 4 million 60,000 150,000 Every 4 s

Riders send their location (a ping) for dispatch from day one, and every 4 seconds once live tracking ships in Module 6. "2 abroad" is Dubai and Singapore, in Module 18.

Two habits hold throughout. About 60% of a day's orders land in the 7pm to 10pm dinner rush, and an order takes about 30 minutes to arrive, about 15 of them on the road with a rider, who often carries two at once in the rush. When a later lesson zooms into one region of the national app (say, 10 cities in the west), it says so.

What to remember

  1. The diagram is a picture of a design. The design is the decisions behind it.
  2. Most decisions fall into four families: data, communication, failure and growth.
  3. Even a one-tap feature hides several decisions. Skip them and they get made by accident.
  4. Constraints (users, team, time, money, existing systems) decide which design is right.
  5. A good design meets the requirements with no more complexity than they need. Extra parts must earn their place.
  6. Write decisions down, including what you gave up and when to revisit.