Part II · Chapter 2 · One Service · lesson 9
The company that built for ten million and got eight thousand
14 min
Builds on Will it fit on one box? and From "we have 50,000 users" to requests per second
Part II · Chapter 2 · One Service · lesson 9
14 min
Builds on Will it fit on one box? and From "we have 50,000 users" to requests per second
Eleven services, three Kubernetes clusters, a managed Kafka, a service mesh, and a GitOps pipeline. Five engineers. Eight thousand users, who between them generated about four requests a second.
It all worked. Nothing ever fell over from load, because nothing was ever remotely close to load.
Before the case file: what was the largest cost of building it this way?
Predict
Case file
Composite, not a real person
One Python application and one Postgres database, deployed by a shell script, serving about four hundred users with no outages worth remembering.
A correct, stable, observable system costing about $4,400 a month to run, consuming roughly forty per cent of the engineering team's time, in which a one-line change took about thirty-five minutes to reach production. Eighteen months later, two engineers moved it back to two application servers and a managed Postgres in a fortnight, for about $800 a month, and no customer noticed anything except that releases got faster.
The mesh bought one checkbox on a questionnaire. Mutual TLS between services was a line item from an enterprise prospect that did not sign. The mesh's own first production incident was a certificate rotation that failed inside a sidecar nobody on the team had read the source of, and every service-to-service call failed for about forty minutes while five people read documentation. The service that was down was not the service that broke.
Kafka carried about twelve messages a second. That is a Postgres table with a status column and a worker polling it, which is what they replaced it with, and which nobody has thought about since. Kafka earns its place when you need replay, multiple independent consumers of the same stream, or throughput a database table cannot carry — none of which was true here.
Three clusters bought environment parity, badly. Development and staging were mirrors of production in topology and nothing like it in data, so the things they caught were deployment-layer problems, which were problems the deployment layer had introduced.
Eleven services bought nothing, because they were not independent. Split by page rather than by data ownership, they all read and wrote the same database, so every meaningful change touched several of them and had to be released together. They were one system with eleven deployment units and eleven times the operational surface.
This is the part the usual version of this story leaves out, and leaving it out is what makes the story useless.
Containerising the application, with base images pinned by digest and built in CI. This survived the migration completely intact. The same images that ran in Kubernetes ran on two virtual machines under a container runtime the following month. It made the build reproducible, it removed an entire category of "works on my machine", and it is the one piece of the original platform nobody has ever proposed removing. If the team had done only this, the eighteen months would have been net positive.
Putting all infrastructure in Terraform from the first day, with no manual changes allowed. This is why the simplification took a fortnight rather than a quarter. The team could read what the old stack actually contained — every security group, every subnet, every parameter — rather than discovering it by breaking things. The new, smaller stack was a reviewable diff against the old one, which also meant the decision could be argued about on its merits with the real list in front of everybody.
Notice the shared property. Both of these are about reproducibility, and neither is about scale. The decisions that aged well were the ones that did not depend on a forecast being right.
Check
The condition is the honest one, and it is in the premise: when the growth that was forecast actually turns up.
At eight thousand users none of it was needed. At eight million, with thirty engineers in six teams that must deploy without coordinating, most of it becomes the straightforward answer: you need orchestration because you have hundreds of containers, you need a mesh or something like it because you have policy to enforce across teams, and you need a log because you have genuinely independent consumers of the same events.
So the criticism is not of the components. It is of the ordering. Every one of those pieces can be adopted in a fortnight when the need is in front of you, with the numbers to size it, by a team that has grown enough to operate it. The same pieces adopted eighteen months early cost forty per cent of a five-person team for the whole of those eighteen months, and bought nothing that could be pointed at.
Which gives the one rule I would actually hand somebody: adopt the complexity when you can name the number that forced it. Not the forecast — the measurement. "Our deploys are blocking three teams" is a number. "We will have ten million users" is a hope with infrastructure attached.
Because it is the only cost in the story that somebody is accountable for.
A cloud bill arrives monthly, has a line for each component, and gets reviewed by a finance function that will ask about it. That makes it the cost most likely to be noticed and the most likely to be fixed — which is exactly why teams end up with the same architecture on smaller instances.
The forty per cent of engineering time has none of those properties. It arrives as a day of cluster upgrade here, an afternoon of certificate debugging there, a week of pipeline work that reads as infrastructure investment in a stand-up. Nothing aggregates it, nobody owns it, and it never appears beside the roadmap items it displaced. The only way to see it is to go looking: count the pull requests that touched the deployment layer against the ones that touched the product, over a quarter, and the ratio is usually a surprise to everyone including the people who wrote them.
That count takes an afternoon and it is the most useful architectural measurement a small team can make.