Intro to System Design7 min · 2 of 64

What system design actually is

Turn a vague product idea into an architecture you can defend, using the constraints and the arithmetic that decide between two reasonable designs.

System design is the process of defining the architecture, components, interfaces, and data of a system so that it satisfies a stated set of requirements. The output is a blueprint: what the pieces are, how they talk to each other, where the data lives, and which failure each choice buys protection against. The work is not producing the drawing — it is being able to say why this drawing and not the other one, in numbers.

The blueprint and its constraints

An architect designing a building does not start by drawing walls. They start with four things, and software design has a direct counterpart for each:

  • The purpose of the building. House, school, or skyscraper. In software these are the functional requirements — what the system must do.
  • The constraints. Budget, plot size, local building codes. These are the non-functional requirements: latency, availability, cost, and the traffic the system must absorb.
  • The materials. Concrete, steel, glass. These are the technologies and components — a relational database, a cache, a queue.
  • The structure. How rooms connect, how load is distributed, where the plumbing runs. This is the architecture.

The analogy holds where it matters: constraints decide the structure, not the other way round. A skyscraper is not a taller house. Splitting requirements into those two buckets is the first move in any design — see functional vs non-functional requirements.

The same phrase at three different scales

"System design" describes three different activities, and confusing them is why the term feels slippery.

A single feature inside an existing application: a new endpoint, a new table, a background job. Framework, datastore, and deployment are already decided. The question is narrow — where does this logic belong, and what does it do to the queries already running.

A small application, say a to-do list: choose a datastore, define the schema and the API, decide what runs synchronously. You control the whole structure, but at a few hundred users almost any coherent choice works. At this scale the design is mostly about keeping later options open.

A large distributed system — search, a social feed, video streaming. Many services across many machines, often many regions. Now the choices are forced rather than free: scalability, availability, and consistency stop being adjectives and become budgets you spend. This is where decisions get expensive to reverse.

A worked example: shortening URLs

"Design a URL shortener" sounds trivial: store the long URL, hand back a short code, redirect on lookup. The design questions appear as soon as you attach numbers. Assume 1 million new links per day and 100 million redirects per day — a modest service at a 100:1 read-to-write ratio.

Writes: 1,000,000 / 86,400 s   ~= 12 QPS average, ~50 QPS at a 4x peak
Reads:  100,000,000 / 86,400 s ~= 1,200 QPS average, ~4,600 QPS at a 4x peak
Storage: 1,000,000 rows/day x ~500 B = 500 MB/day = ~180 GB/year

Three conclusions fall out of that arithmetic, none of them visible in the prose version of the problem.

The write path is nearly free: 50 QPS at peak, against the roughly 5,000 simple QPS a single commodity Postgres box handles. The read path is the system. At 4,600 QPS peak it sits close enough to that ceiling that one traffic spike takes the service down, so reads go through a cache — at a 90% hit rate the database sees about 460 QPS instead of 4,600. That is the whole argument for caching, and it is made of two numbers.

Storage is a non-problem. 180 GB a year fits on one disk, so nothing here justifies a distributed store. Sharding this database would be designing for a scale nobody asked for.

The whole course in one small system. The write path is rare and boring; the read path is where every design decision — cache, redirect code, key generation — actually lives.
A URL shortener: write path, read path, and the cache in betweenPOST /shortenGET /abc123insert rowlookup codemiss on ~10% ofreads301 to the long URLCreatorVisitorApp serverRedis cachecode → URLPostgrescode, URL,created

Scroll to zoom · drag to pan · 0 fits · Esc closes

The diagram makes the asymmetry visible: two paths through the same data, one at 50 QPS and one at 4,600, with a cache on only the busy one.

The questions still open are the actual design work. How are codes generated — randomly, or from a counter? A counter is compact and collision-free but leaks how many links exist and needs coordination across app servers; random 7-character codes need a uniqueness check on write, affordable at 50 QPS. What is the latency budget for the redirect? A cache hit is a sub-millisecond memory lookup plus a 0.5 ms datacenter round trip, but a user in India hitting a server in US East pays ~200 ms in network time alone. If the target is a 50 ms p99 redirect worldwide, no amount of server tuning reaches it and the design changes shape: the lookup moves closer to the user, which is a CDN or edge decision, not a database one.

Design is not boxes and arrows

The drawing is the artefact. The judgment is the work, and it shows up in four places.

Trade-offs. Every choice costs something. Strong consistency costs latency, because a read waits for agreement. Caching costs freshness. A queue buys the write path a shock absorber and pays with eventual consistency and duplicates — at-least-once delivery means duplicates arrive, which means consumers must be idempotent. Naming the cost is what separates a decision from a preference.

Principles over product names. Knowing a technology exists is not knowing why it is used or how it behaves when it breaks. "We will use Kafka" is a brand. "We need a durable log so a slow consumer cannot drop events" is a reason, and the brand follows from it.

Iteration. Designs are drafted, then broken. Produce a high-level structure, find the component that fails first under the numbers, fix it, find the next one. The horizontal vs vertical scaling choice usually only becomes obvious after the first bottleneck appears.

Communication. A blueprint exists so other people can build from it. A design that only makes sense inside your head has failed at its one job, and a design nobody else can explain is one nobody can debug at 3am.

In an interview

The interviewer is testing whether you treat an underspecified prompt as a design problem or a drawing exercise. Every prompt — "design a URL shortener", "design a news feed" — is missing the scale, the features, and the latency targets on purpose. Producing components before producing constraints is the failure mode being watched for.

Say the constraints out loud before you draw: "Before I put anything on the board — how many new links a day, how many redirects, and what latency are we targeting?" Convert the answers to QPS in front of them and let the numbers pick the components. When you name a component, attach the reason: "a cache on the read path, because 4,600 QPS peak against a box rated for about 5,000 leaves no headroom."

The specific mistake that loses points is designing for a scale nobody stated. Sharding, multi-region replication, and a service mesh in front of a system doing 12 write QPS reads as an inability to size a problem, not as ambition. Match the simplest sufficient design to the stated constraints, then name the number that would make you change it — see what a system design interview actually tests for how the hour is scored.

Check yourself

Redirect traffic on the shortener grows 10x, to 1 billion a day. Do you shard the database? Show the arithmetic.

1,000,000,000 / 86,400 s is roughly 12,000 QPS average, about 48,000 QPS at a 4x peak — far past what one box serves, but the cache tier absorbs it, not the database. At a 95% hit rate the database sees about 2,400 QPS at peak, under the roughly 5,000 simple QPS one commodity Postgres box handles, and storage is still only ~1.8 TB a year. Scale the cache tier first, and state the trigger: "I shard when cache misses alone sustain more than about 4,000 QPS, or when the working set stops fitting in memory."

A stakeholder asks for "a fast redirect". What do you turn that into before you design anything, and what does the answer rule out?

A percentile, a number, and a scope: "p99 redirect under 50 ms, measured for users in our main region." p99 rather than the average, because the mean hides the tail users actually feel. If the requirement is 50 ms p99 worldwide, a single-region deployment is already ruled out — an India to US East round trip is ~200 ms of network time before any server does anything.