Skip to content

Expert · Deployment and scaling · topic 51 of 54 · 30 min

System design: planning a big backend

Explanation

System design is planning how the parts of a large backend fit together before you build. You start with requirements and rough numbers, then sketch the API, the data model and a diagram, and finally look for bottlenecks. Start with a well-organised single app (a modular monolith), and add queues, caches and separate services only when the numbers show you need them.

A simple way to approach any design question

  1. Requirements: what must it do (features)? How fast, how reliable, how big (non-functional requirements)?
  2. Numbers: how many users, requests per second, how much data per year? Rough is fine.
  3. API: the main endpoints, with inputs and outputs.
  4. Data model: the main tables or collections and their keys.
  5. High-level diagram: clients, load balancer, app servers, database, cache, queue, CDN.
  6. Bottlenecks: what breaks first at 10 times the traffic, and how you would fix it.
Think of it like this: Planning a system is like planning a big Indian wedding. First you ask how many guests (numbers) and what they need (requirements). Then you book the hall and caterers (components), plan the buffet lines so no line gets too long (load balancing), and keep extra plates ready for the rush after the ceremony (scaling for the peak). Nobody starts by choosing napkin colours.

Back-of-the-envelope numbers

output
Daily active users:        10,00,000 (10 lakh)
Requests per user per day:  20
Requests per day:           2,00,00,000 (2 crore)
Seconds in a day:           86,400
Average:                    2 crore / 86,400 = about 232 requests per second
Peak (5x, evening rush):    about 1,160 requests per second

Storage: 1 lakh new records a day x 500 bytes = 50 MB a day, about 18 GB a year
Rough sums tell you if one server is enough or you need many, and if storage is small or huge.

Monolith or microservices?

A monolith is one app that does everything, deployed as one unit. Microservices split the system into many small apps (users, orders, payments), each with its own database and deploys, talking over the network. Microservices let big teams work and deploy separately, but they add real costs: network failures between services, harder debugging, data spread across databases and much more to monitor.

Modular monolithMicroservices
DeployOne appMany apps, each on its own
Calls between partsFunction calls (fast, reliable)Network calls (slower, can fail)
Good forSmall and medium teams, most productsMany teams that need to ship separately
Main riskBecoming a tangled "big ball of mud"Complexity, outages between services
Tip: Start with a modular monolith: one app, but with clear modules (users, orders, payments) that talk only through defined functions. If one module later really needs to scale or deploy on its own, you can cut it out cleanly.

Queues, events and consistency

A message queue holds jobs or events until a worker takes them, for example RabbitMQ, Kafka, Amazon SQS or Redis streams. Instead of doing everything inside the request, the API saves the order, puts an "order.placed" event on the queue and answers quickly. Workers then send the SMS, update the restaurant screen and record analytics. If a worker is slow or down, the events wait safely instead of failing the order.

When data is copied across servers, you sometimes have to choose. If the network between them breaks, a system can either refuse some requests so it never shows wrong data (consistency first), or keep answering with data that may be slightly old (availability first). A bank balance or a train seat needs consistency. A like counter or "people viewing this hotel" can be a little out of date.

Worked example: a URL shortener

  • Requirements: make a short link from a long URL; visiting the short link redirects fast; links last for years; about 1 lakh new links and 1 crore visits a day.
  • API: POST /api/links { url } returns { code: "aZ3k9Qx" }; GET /:code answers 301 or 302 with a Location header.
  • Data model: links(code PRIMARY KEY, long_url, user_id, created_at).
  • Codes: take a unique number from a counter (or the row id) and write it in base 62 (0-9, a-z, A-Z). 7 characters give 62^7, about 3.5 trillion codes.
  • Reads are 100 times more than writes, so cache popular codes in Redis and add read replicas; count clicks through a queue so redirects stay fast.
javascript
const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

function encodeBase62(n) {
  if (n === 0) return '0';
  let out = '';
  while (n > 0) {
    out = ALPHABET[n % 62] + out;
    n = Math.floor(n / 62);
  }
  return out;
}

console.log(encodeBase62(125));        // '21'
console.log(encodeBase62(1000000000)); // '15ftgG'
Turning a counter into a short code. The same idea as writing a number in base 10, but with 62 digits.

Short case: the Tatkal rush

Tatkal train booking opens at a fixed time each day, so lakhs of people press "Book" in the same minute for a few thousand seats. A design for this kind of rush usually: puts users in a virtual waiting queue so the booking service gets a steady flow; keeps seat counts in a fast cache for display, but makes the final seat check inside a database transaction so a seat is never sold twice; rate limits each user and IP and blocks bots; serves all static pages from a CDN; and makes payment steps idempotent (safe to retry) so a double click does not charge twice.

Careful: Do not copy big-company designs for a small app. A design for 10 crore users is the wrong design for 10,000 users: it costs more, is slower to build and has more ways to break. Design for your numbers, with room to grow about 10 times.

Watch the lesson

System design: planning a big backend · 1/7

System design

System design means planning how the parts of a big backend fit together, before you build.

Prefer a video by a teacher? Find free videos on “system design for beginners url shortener” on YouTube ↗

In real life: A food delivery app during an IPL final

During a big cricket final, orders on a food delivery app jump at the innings break. The team planned for this with numbers: normal peak 500 orders a minute, expected 5,000. The order API stays a modular monolith on many stateless servers. Placing an order only saves it and puts an event on a queue, so the API answers in under a second. Workers then notify restaurants, assign riders and send SMS at their own pace. Restaurant menus are cached, and images come from a CDN. Nothing here is exotic; it is the basic toolkit, sized with real numbers.

Practice (0/3 done)

Task 1 · Code · Javascript

Write estimateRps(dailyUsers, requestsPerUser, peakFactor) for back-of-the-envelope sums. perDay = dailyUsers x requestsPerUser. average = perDay / 86400, rounded UP to a whole number. peak = perDay x peakFactor / 86400, rounded UP. Return { perDay, average, peak }.

Task 2 · Code · Javascript

Short links use base 62. With the alphabet "0-9, then a-z, then A-Z" (given in the starter), write encodeBase62(n) for a whole number n >= 0 (0 gives "0") and decodeBase62(code) that turns a code back into the number. Example: 62 is "10" and 3843 is "ZZ".

Task 3 · Practice

Design a flash sale for a shopping app: 10,000 phones at a special price go on sale at 12:00 noon, and 20 lakh people are expected to try in the first five minutes. Use the six steps from this lesson.

Quick quiz

Question 1

1.What should come FIRST when answering a system design question?

Question 2

2.For a new product with a small team, what is usually the best starting point?

Question 3

3.Why put "order.placed" events on a queue?

Question 4

4.Which data most needs consistency over availability?

Question 5

5.How many different codes can 7 base 62 characters make?

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Scaling: handling more users