The Invoice ■ Episode 01
"But deployments are cleaner!"
That sentence has launched more Kubernetes clusters than any legitimate scaling requirement. It sounds persuasive. It sounds modern. And it is the argument that transforms a perfectly functional application into a distributed system before anyone has paused to ask whether a distributed system was actually necessary.
Let us examine the invoice.
The Technical Invoice
A function call within a single process completes in roughly 1 to 10 nanoseconds. A network call between services -- even on the same rack, even in the same data centre -- takes 0.5 to 1 millisecond. That is a factor of one thousand to one million before any business logic executes. Not occasionally. On every request, through every service boundary, without exception.
The latency is not the worst of it. The combinatorial explosion is. The number of potential network paths between n services follows the formula n × (n − 1). Three services yield six paths -- manageable, even pleasant. Ten services produce ninety. Fifty services give you 2,450. Each path is a latency risk, an authentication checkpoint, a serialisation boundary, and an attack vector. Each one must be monitored, secured, retried on failure, and debugged when it misbehaves at three in the morning.
Three services: you can draw the diagram on the back of an envelope. Ten: you need a whiteboard. Fifty: you need a dedicated platform team and a service mesh, which is itself a distributed system that requires its own monitoring, its own debugging, and its own team. The meta-complexity is not a joke. It is a line item.
The Organisational Invoice
Each service needs ownership. Ownership needs a team. Teams need coordination. Coordination needs meetings, Slack channels, API contracts, versioning strategies, and deprecation policies. What began as "independent deployment" quietly becomes: twelve teams, twelve coding standards, twelve interpretations of "clean architecture," and one rather exhausted architect attempting to maintain consistency across a system that nobody fully understands any longer.
The irony is rarely acknowledged. Microservices are sold as a means of reducing coordination overhead. In practice, they multiply it. A function call inside a monolith requires no API contract, no versioning, no backward compatibility negotiation. A network call between services requires all three, plus authentication, serialisation, error handling for partial failures, and a shared understanding of what "eventually consistent" means in each specific context.
Conway's Law is not a suggestion. It is a description of reality. You do not choose your architecture independently of your organisation. If you have three teams, you will have three services -- or you will have one service and a great deal of conflict. Microservices do not decouple teams. They formalise the coupling into network protocols and hope nobody notices.
The Hidden Invoice
Senior engineers debugging distributed transactions instead of building features. Platform teams maintaining service meshes instead of delivering value. On-call rotations multiplied by the number of services. Incident response that requires correlating logs across fifteen systems to find the one that dropped a message.
Martin Fowler -- who coined the term alongside James Lewis in 2014 -- has been saying "Monolith First" since 2015. The man who named the pattern recommends against starting with it. That alone should give one pause.
You shouldn't start a new project with microservices, even if you're sure your application will be big enough to make it worthwhile.
One might reasonably ask why an industry that claims to value evidence-based decision-making has so thoroughly ignored this particular piece of evidence.
The Root Cause Nobody Mentions
Here is the part that tends to make people uncomfortable.
Node.js cannot utilise more than one CPU core per process. It is single-threaded by design. Its event loop handles concurrency through asynchronous I/O, which works splendidly for I/O-bound workloads, but the moment you need CPU-bound computation, you hit a wall. One core. One thread. One limit.
Rather than fixing the runtime -- rather than adopting a language with proper threading -- an entire industry decided that the solution was to split applications across the network. If one process cannot use eight cores, run eight processes. If eight processes on one machine are not enough, distribute them across the data centre. If the data centre introduces latency, add a service mesh. If the service mesh is complex, hire a platform team.
We invented distributed systems complexity to work around a single-threaded JavaScript runtime. Quite the engineering achievement, in its way.
The Question Nobody Asked
What actually prevents one from structuring a monolith modularly?
Nothing. Languages with proper threading -- Go, Rust, Java, C# -- scale vertically before one ever needs to think about service boundaries. A well-structured monolith with clean module boundaries, clear internal APIs, and proper separation of concerns gives you every benefit microservices promise, minus the network. Minus the latency. Minus the distributed tracing. Minus the platform team.
The internal function call costs nanoseconds. The network call costs milliseconds. The organisational overhead costs months. The debugging costs sanity.
Netflix runs over 700 microservices. Netflix also has thousands of engineers, its own CDN, and infrastructure requirements that would make most companies weep. Microservices make sense at Netflix scale. For the other 95 per cent -- startups, mid-sized companies, e-commerce platforms with a few hundred thousand users -- you are paying enterprise complexity for problems a well-structured monolith solves in milliseconds rather than network hops.
The Verdict
Microservices are a tool. Like all tools, they have a context in which they are appropriate and a far larger context in which they are not. The problem is not the pattern. The problem is the cargo cult: the assumption that what works at Netflix scale works at every scale, that what impresses in system design interviews is the same thing that ships products.
It is not.
The architecture that ships products is the one you can reason about, debug at three in the morning, and explain to a new team member in under an hour. For most organisations, that architecture is a monolith. A well-structured, modular, properly tested monolith that calls functions in nanoseconds rather than services in milliseconds.
But it does not look as impressive on a conference slide. And therein, one suspects, lies the actual root cause.