Deep Engineering

System Design

Architectural decisions that have an author, a date and a reason. Every pattern is read from the primary source — the text that first described it and the specification that says what it must do — never from retellings.

Sections
6
Articles
8
SOURCES
67
Difficulty
8 articles · matching

Sections

Articles

AdvancedPublishedBackend for Frontend: the layer that knows who is askingThe pattern was described in 2015 for the sake of speed and team autonomy. Eleven years later an IETF draft started strongly recommending it for something its authors never wrote about — keeping the token out of the browser entirely.bffapi-designweb-securityfrontend-backendcsrf30 MIN12 SOURCESRead →AdvancedPublishedThe stretched cache: why the analogy with the database is false"Our database is stretched across two data centres with replicas and it is fine" — and about the database that is true, but "stretching" means different things for a database and for a cache. A cache rests on a comparative property: going to it has to cost less than the source operation it replaces. On a cheap read by key across a link between data centres nothing of that property survives — 41.09 ms against 0.09 ms to the database at hand. And a cluster split evenly between the two sites failed in both halves when the link went down — including the half that held the majority of masters. This revision also checks the measuring rig itself, and one published number turned out to be measuring a defect in the instrument rather than distance.redisvalkeycachingreplicationredis-cluster70 MIN10 SOURCESRead →AdvancedPublishedTwo ways to build Valkey out of several nodes: Cluster and SentinelThe difference is not fault tolerance — both have it. Four things separate them at once: how the data is laid out, who decides on a failover, who answers the client's question of where to send the command, and what the client itself has to be able to do. Plus a fifth dimension people forget most often: the version. The rule "a cluster has exactly one database" is true for Redis and for Valkey before 9.0, and false for Valkey 9 — and it is re-measured here on two clusters differing by a single config line.valkeyrediscachingclusteringsentinel50 MIN8 SOURCESRead →AdvancedPublishedIsolation levels: the price of SERIALIZABLE is retries, not a slow queryThe standard's table says what a level is forbidden to allow, not what your database does. PostgreSQL departs from it in both directions at once: a phantom read does not occur at REPEATABLE READ although the standard permits it there — while write skew passes straight through, and that anomaly is not in the table at all. The measurement adds the second half: in this workload REPEATABLE READ and SERIALIZABLE are indistinguishable from each other — the gap between them is smaller than the round-to-round spread — and the price is paid in retries.postgresqltransactionsisolation-levelsserializablewrite-skew30 MIN2 SOURCESRead →AdvancedPublishedThe transaction outbox: what it guarantees, what it does not, and what it costsThe event becomes a row in the same database, and then it shares the order's fate. The interesting part starts after that: a relay that remembers the id of the last row it sent loses events silently, and the delivery order the pattern promises is not delivered at all — neither by polling the table nor by reading the WAL.outboxpostgresqlcdckafkaidempotency30 MIN9 SOURCESRead →AdvancedPublishedSharding: the key decides where every query goesSpreading the data over nodes is half the job; the other half is what stops working afterwards. Computed here and measured on a live PostgreSQL: going from eight shards to nine, hash-mod placement moves 88.9% of the keys where 11.1% is the minimum; on a postgres_fdw stand a query without the shard key pays five exchanges to every shard, and two settings overlap only two of them; a transaction through postgres_fdw that touched two shards left a row behind after the client's error in three cases out of four.shardingpartitioningpostgresqlpostgres-fdwconsistent-hashing45 MIN7 SOURCESRead →AdvancedPublished3.13A DI container: what it actually does for you — and how dishka differs from dependency-injectorA container does not solve “reduce coupling”; it solves two concrete jobs: assemble the object graph and draw the boundary of how long those objects live. The main divergence between the two libraries is on the second one. From that follows both the fact that two repositories of one request end up in different transactions, and the answer to which of them is faster — which depends on the first question.dependency-injectiondi-containerdishkadependency-injectorscopes25 MIN6 SOURCESRead →AdvancedPublishedLoad balancing: who picks the server, and what they know when they pickThe earlier a decision is made, the less is known about it. DNS picks a server minutes before the request and knows nothing about it; an L7 balancer picks at request time and knows everything. That gap explains both the layering and the headline result of the queueing model: least connections and least response time are the best choice exactly as long as the load signal is fresh.load-balancingdnsl4l7power-of-two-choices40 MIN13 SOURCESRead →

Recommended order

Knowledge graph

NON-LINEAR · PREREQUISITE AWARE

Topics are connected, and the connections here were not drawn by hand — they were counted: every article states what it builds on and what it sits next to, and the picture is assembled from those statements. A dashed circle is a topic the articles already call related but that has not been written yet.

9 · 7 nodes and edges

Edges as a list

No connections: di-containers