Architecture Evolution & Complexity Ledger
One app server and one database at 100 daily users. Raise the traffic, read the bottleneck off the capacity model, then add a component — and let the readings say whether it was justified. Every component answers the same four questions: what does it solve, what does it introduce, what happens when it fails, and did it move the worst reading.
The rule is one sentence: add a component only when a reading says you must. At 100 users nothing is a bottleneck, so nothing is justified — a cache there lowers a database reading that was already at a fraction of a percent. At ten million reads the database is saturated, and a cache moves it because the load is reads. Then try the same cache with the read-heavy toggle off and watch the writes it cannot touch.
Complexity ledger
Every component you added, answering the questions §119 says it must — and whether the readings justify it right now.
Nothing added
One app server and one database. The ledger is empty, and at low traffic that is the right answer.
Would this help?
Every component not yet added, judged against the current readings. Justified means it moves the worst reading; nothing else counts.
At 100 users every reading is ok. Database index has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. Cache (Redis) has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. Read replica has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. Bigger database instance has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. Queue + worker has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. CDN for images has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. Load balancer has nothing to fix; it is complexity without a reason.
More app servers needs Load balancer first; without it it changes no reading.
At 100 users every reading is ok. Text index on product name has nothing to fix; it is complexity without a reason.
At 100 users every reading is ok. Search engine (Elasticsearch) has nothing to fix; it is complexity without a reason.
Kafka would solve: Decoupled producers and consumers, replayable event history, and many teams consuming the same stream without coordinating deploys. This simulation models one team and one service, so it cannot show that benefit: no reading here moves when you add it. At 100 users the worst reading stays no bottleneck with or without it.
Microservices would solve: Independent deploys and independent scaling per team, with a boundary that stops one team's change from breaking another's code. This simulation models one team and one service, so it cannot show that benefit: no reading here moves when you add it. At 100 users the worst reading stays no bottleneck with or without it.
Kubernetes would solve: Scheduling, restarting and rolling out many services across many machines with one declarative description. This simulation models one team and one service, so it cannot show that benefit: no reading here moves when you add it. At 100 users the worst reading stays no bottleneck with or without it.
The model is one team and one service: requests per day, reads against writes, a database with a capacity, one app server, a connection pool, a LIKE query over a growing catalog. It cannot show what Kafka, microservices or Kubernetes solve — independent deploys, decoupled producers and consumers, scheduling many services — because those are team-scale and organisational problems, and no reading here moves when you add them. That is a limit of the model, not a verdict on the tools. Every number is illustrative.