Skip to content
CalliCoder

2 tutorials

System Design tutorials

Every System Design guide on the site, newest first. Each one is built around code you can run.

System design writing tends toward diagrams of boxes with no numbers attached. These articles try to stay concrete: what a design costs, where it fails, and what has to be true for it to be the right choice.

The topics include generating unique identifiers in a distributed system without a central coordinator, caching strategies and the invalidation problem underneath them, and the engineering principles that shape how a codebase absorbs change. Where a design has a well-known failure mode, the article names it rather than presenting the happy path alone.

The framing throughout is that every architectural decision buys something and pays for it somewhere else. An article that presents an approach with no cost has not finished examining it.

Identifier generation is treated in depth because it is a compact example of the whole discipline. A single auto-increment column is simple and does not survive sharding. A UUID survives anything and destroys index locality. A Snowflake-style identifier fixes both and introduces clock skew as a new failure mode. Three designs, three different things going wrong.

The same treatment applies to caching. Cache-aside is straightforward until two requests miss at once; write-through keeps the cache warm and slows every write; TTLs bound staleness without ever eliminating it. The article names which problem each strategy leaves unsolved, because that is what determines whether it fits.

None of this assumes a particular cloud. The mechanisms — partitioning, replication, caching, identifier generation — predate every managed service that now packages them, and knowing what the managed service is doing is what lets you tell whether it fits.

Consistency and availability come up throughout, and the guides try to be specific rather than invoking CAP as a slogan. What matters in practice is which operations tolerate stale reads, how long a partition can last before a client notices, and what the system does when it cannot reach a dependency — degrade, queue, or refuse. Those are answerable questions about a particular design; the theorem on its own settles none of them.

Two further questions run through the section. The first is what a design costs to operate, not just to build — a scheme that needs coordinated clocks or a manual failover step has moved work from the code to whoever is on call. The second is how it fails partially, since total failure is rare and usually obvious, while a component that is slow rather than dead is the case most systems handle worst.

These are the articles with the fewest code samples and the most trade-offs, which is deliberate. A distributed identifier scheme, a cache eviction policy or a layering decision cannot be evaluated by reading an implementation; it is evaluated by asking what it costs when the system is under load, when a node is lost, or when the requirement changes.

So each piece tries to state the constraint that makes the design necessary before showing the design, and to name the cheaper option that is usually correct. A pattern presented without the case for not using it is not much use.

The scope is deliberately narrow. These are not architecture surveys; each one takes a single decision that recurs in ordinary backend work and follows it to the point where the trade-off becomes concrete. Generating an identifier that is unique across machines, deciding what to evict from a cache and how to know, choosing where a responsibility belongs between two layers. Breadth is easy to find elsewhere and rarely helps; the useful part is watching one decision be made carefully.

All System Design tutorials

Frequently asked questions

Is this material aimed at system design interviews?

It overlaps, but the framing is what you would actually build and operate rather than what fits on a whiteboard in forty minutes.

How much scale do the articles assume?

Enough that a single database no longer answers the question. Where a simpler design would do, the articles say so.

Are there code examples?

Where code clarifies the mechanism — an identifier generator, a cache-aside implementation — yes. Where the decision is architectural, the trade-off is the content.

Do the articles cover failure modes?

Yes. A design is described together with the conditions under which it stops working.

Why not just use UUIDs for everything?

They are unique and they destroy index locality, which shows up as write amplification on a large table. The trade-off is the whole point of the topic.

Is cache invalidation covered?

Yes — as the hard part rather than a footnote, including what each strategy leaves unsolved.