← Back to all posts

Microservices Were Never a Good Idea (And Agents Are the Final Proof)

2026-08-13 · 24 views · 12 min read

Intro

Here's an opinion I've been carrying around for years, and it only got louder: microservices were never a good technical idea. They were an organizational idea wearing a technical costume. And now that AI agents are rewriting how software gets built, the costume is coming off.

Let me be clear about what I'm not saying. I'm not saying distributed systems are bad. I'm not saying Netflix should run on a Raspberry Pi. I'm saying: for the overwhelming majority of teams and products, splitting your application into microservices made everything worse: Slower to develop, harder to test, more expensive to run. And the reasons people gave were mostly rationalizations.

Fight me. But first, read the argument.

Conway's Law cuts both ways

The standard origin story: company adopts agile, restructures into small cross-functional teams, and then pikachu-face-here the architecture reorganizes itself along team boundaries. That's Conway's Law: systems mirror the communication structure of the organization that builds them.

Here's the part people skip: Conway's Law is not a disease you cure with microservices. It's a law of nature. Your architecture will reflect your org chart whether you want it to or not. The question is only whether you acknowledge it.

Microservices were the acknowledgment. They were a way to give 15 teams independent deployability so they wouldn't step on each other. That's a real problem! But notice: it's a problem of organizational coordination, not of technology. We took an org design decision and dressed it up as an architecture decision. And then we spent a decade paying the technical price. Price as in: Network hops, distributed transactions, schema drift, observability theater, .. and all for what was essentially an HR restructuring.

And here's the kicker: the org problem has a different solution now. More on that in the last section, because it's the whole point.

The compiler is the best integration test you will ever have

In a monolith, when you break an interface, the compiler tells you. Immediately. For free. In Rust or Go, cargo build or go build is your cross-module integration test suite, and it runs in seconds on every change.

In a microservice world, the equivalent failure is discovered in staging. Or in production. At 3am. By your customers.

But contract testing! Protobuf! OpenAPI codegen! - sure, and I've seen exactly two organizations in the wild that actually maintain their contracts with the discipline this requires. Everyone else is hand-gluing JSON and praying. And even with perfect contracts, you've only covered type mismatches. The actual killers of distributed systems are runtime behaviors: timeouts, retries amplifying load, partial failures, version skew between deployments. No compiler saves you from those. You moved a class of bugs from caught at compile time to caught by PagerDuty and called it progress.

The DST argument.. the bug class you can't afford becomes testable

It gets better. The single most powerful testing technique for distributed-state logic is deterministic simulation testing. The FoundationDB/TigerBeetle approach: run your entire system against a simulated network, a simulated disk, a deterministic scheduler, and inject every fault imaginable. Drop packets. Reorder messages. Crash nodes mid-commit. Because everything is deterministic, every failure is reproducible from a seed.

Here's the thing: DST needs you to control all the code and the scheduler. In a monolith, that's nearly free. TigerBeetle does exactly this, and yes, TigerBeetle is itself a distributed system, so DST isn't tied to monoliths. It's tied to a single, controlled execution context. Across a fleet of independently deployed microservices, owned by different teams, running different versions? Practically impossible. You don't control the network's scheduler, and you definitely don't control your neighbor service's deploy pipeline.

So the honest framing isn't monoliths are better tested. It's: the most expensive bug class in existence (distributed state logic under faults) is only testable at all in a unified codebase. We gave that up for independent deploys. Does not sound like a good deal to me.

64 cores, one Rust binary, one SQLite file

Now the scaling argument, which is the one remaining technical leg microservices stand on. Let's do the math people don't do.

A boring modern server has 8-64 cores. Put a Rust backend on it with SQLite in WAL mode (Litestream or Turso for replication) or Postgres. You're looking at tens of thousands of reads per second and five-digit writes per second, from one machine, with one deployable artifact, and one process to debug. Hacker News — a top-1000 website — famously runs on a single server. Stack Overflow served millions of users from a handful of machines for years.

Until you outgrow that, you don't have a scaling problem. You have a meeting habit or worship a scrum god in the hope for salvation.

And if you genuinely do have spiky, unpredictable load? Serverless exists. Burst to a thousand parallel executions, pay for what you use, no k8s cluster to babysit. Note that any microservice you run on Kubernetes has to be stateless anyway — or it's garbage. So you've already accepted the serverless constraint; you just added YAML. But my team really has no experience doing serverless! - if you can't build serverless, you shouldn't build microservices, you probably shouldn't build distributed systems at all.

Let me be honest about the limits before the comments section does it for me:

  • Multi-region. If your users are in Sydney and Frankfurt, the speed of light beats your 64 cores. That's the one scaling argument that survives — and it's an argument for edge placement and read replicas, not for microservices.
  • Single-writer ceilings. Write-heavy ingestion workloads will hit the SQLite/Postgres single-writer wall before they hit 64 cores. Fair. Shard the data, not the codebase.
  • Blast radius. One process, one failure domain. For most products that's an acceptable, explicitly chosen bet — but it is a bet. Own it.

Notice what all three have in common: they're infrastructure problems. Which brings me to my favorite example.

The Postgres lesson

PostgreSQL handles MVCC, a query planner, WAL, and synchronous + logical replication across continents — the actual multi-zone problem that startups staff entire platform teams for. Guess how many people build it.

The core team is 7 people. Total committers after nearly 30 years: 31. In 2025 there were 266 principal commit authors — but 66% of new code lines came from 26 people, and 90% from 67. One of the most complex, most reliable pieces of software ever written, built by a group that fits in a meeting room.

The lesson isn't be smart like Postgres hackers. The lesson is about where scaling problems should live. Postgres solved replication once — correctly, generically, tested to death — and millions of applications get multi-zone HA as a free side effect. Compare that to the microservice default: 200 companies each solving distributed-state consistency badly, in-house, per service fleet, forever.

Scaling belongs in the infrastructure layer, solved by a few dozen world-class people. Your application code should be a monolith that rides on top of it.

The 20-developer wall

This is the claim that will lose me readers, so let me phrase it precisely:

Past roughly 20 good developers, a product stops being a product. It becomes an organization with a codebase attached.

Above that threshold, output doesn't scale — coordination cost does. You get alignment meetings, platform teams serving product teams serving feature teams, and architecture that exists to route around people rather than to serve users. Postgres — again — is existence proof that 20–30 excellent engineers is not the ceiling of small projects. It's the ceiling of everything. If the database under half the internet gets built by ~26 core people, your CRUD app does not need 15 teams.

But Chrome! Windows! SAP! — name one of those that's famous for shipping fast. They're not products anymore; they're accumulations of products that were never separated. If you need more than 20 developers, you don't have one big product. You have several, pretending to be one, and the honest move is to split the products — with hard, thin contracts between them — not to explode the application into services.

Context windows are the new Conway's Law

And now the part that's no longer a hot take but a new reality.

AI agents work dramatically better in a single, statically-typed repository with fast feedback loops than across 40 repos with separate pipelines, versions, and deployment contexts. One cargo check gives an agent total, immediate, reliable feedback over the entire system. A microservice fleet gives it a keyhole view of one service at a time and a prayer about the rest.

I learned this vibecoding a full-stack Rust SaaS — ~18k lines, zero lines typed by hand, shared DTO crate between frontend and backend. When the DTO changed and the frontend didn't, cargo build failed. The compiler was the code reviewer. That workflow is impossible across service boundaries — it's exactly the oops, we forgot to update the API call bug class that microservices reintroduce, now with an AI generating the mismatches at 10x speed.

This inverts Conway. Remember: microservices solved team coordination, because there was a ceiling on how much codebase one team could hold in their heads. Agents raise that ceiling into orbit. A small team with agents can now move a codebase that used to require 15 teams — and would therefore have required, by Conway's Law, 15 services. The organizational justification for microservices is evaporating in real time:

  • Technical scaling? Solved by hardware, SQLite/Postgres, and serverless for the spiky cases.
  • Testability of distributed logic? Strictly better in a monolith, and only there (DST).
  • Team coordination? Agents are dissolving the constraint that made it binding.

Context windows are the new Conway's Law: your architecture now mirrors what fits in an agent's context. One repo, one type system, one build. That's the architecture agents supercharge — and monolithic development in Go and Rust, with a compiler as a reviewer and agents as force multipliers, has become insanely fast.

Harness > model: it's the cycle time, stupid

But why exactly does the monolith win with agents? It's not magic, and it's not just context size. It's cycle time.

Here's the framing: write one prompt that produces the 100% correct solution to any problem is a nearly unsolvable problem — and it will remain nearly unsolvable no matter how good the models get, because it's really the problem of specifying software perfectly upfront. We've been failing at that since waterfall. The much easier problem is: iterate until all tests are green. Generate, run, read the failure, fix, repeat. Any mediocre model with a good loop beats a genius model with a bad one.

And that is the story of the last twelve months of AI coding progress. The gains came from harnesses, not just models — better loops, better tool feedback, better test integration. Harness > model. Everyone building agentic systems figured this out; almost nobody applied the insight to their architecture.

So what makes a harness good? Two things: short cycle time and sharp tests. And this is where the monolith doesn't just win — it wins by orders of magnitude.

Sharp tests are the classic test pyramid, and monoliths are where the pyramid actually exists: always an order of magnitude more unit tests than integration tests, more integration than system-integration, more of those than end-to-end. The base of the pyramid runs in milliseconds, in-process, no network, no fixtures, no docker-compose orchestra. Each layer up is an order of magnitude slower — which is exactly why the pyramid shape matters: the agent's inner loop lives at the bottom.

Now look at the microservice version of the same loop. Unit tests still exist, sure. But the moment a change crosses a service boundary — which, remember, is the entire point of having services — the agent's integration test needs running dependencies, a deployed environment or a container fleet, seeded state across services, and matching versions of everything. Cycle time goes from seconds to minutes or tens of minutes or even hours to pull-up a complex helm chart. The pyramid inverts into the famous ice-cream cone, and the agent's loop degrades from compile, unit test, done to deploy, wait, pray, read distributed traces.

Do the arithmetic: an agent that iterates in 10 seconds explores a hundred solution attempts in the time an agent with a 15-minute loop explores one. Iteration speed is not a convenience. It is the algorithm. A monolith with a sharp pyramid gives the agent a tight, fast, reliable fitness function; a microservice fleet gives it a slow, flaky, partial one. Architecture is now harness design — and the monolith is the best harness money can't buy.

So what should you do?

If you're starting something new in 2026: one binary, one repo, one typed language with a real compiler and a load of high-quality pre-training in LLMs: basically Rust or Go. And - it hurts me to say this, but: It really doesn't matter that much anymore. With LLMs abstracting the nitty gritties away from the feature, your average team will work good with both languages, and you will need two or three really really senior language specific people to fix those deep issues.

Good databases (Either embedded or Postgres). Serverless for the spiky edges. Split when — and only when — you're splitting products, not functions.

And if you're sitting on 60 microservices right now? I'm not telling you to merge them tomorrow. I'm telling you to stop feeling guilty about the monolith you secretly wish you had.


  • Obviously written in a monorepo.*

Sources

  1. M. E. Conway, How Do Committees Invent?, Datamation 14(4), 1968 — the original paper behind Conway's Law. http://www.melconway.com/Home/Committees_Paper.html
  2. N. Dragoni et al., Microservices: yesterday, today, and tomorrow, arXiv:1606.04036, 2016 — early systematic take on what microservices actually are and cost. https://arxiv.org/abs/1606.04036
  3. S. Ghemawat et al. (Google), Towards Modern Development of Cloud Applications, HotOS 2023 — write as a logical monolith, let the runtime decide deployment topology; the ServiceWeaver paper. https://sigops.org/s/conferences/hotos/2023/papers/ghemawat.pdf
  4. M. Fowler, MonolithFirst, 2015 — the classic almost all successful microservice stories start with a monolith that got too big. https://martinfowler.com/bliki/MonolithFirst.html
  5. J. Zhou et al., FoundationDB: A Distributed Unbundled Transactional Key Value Store, SIGMOD 2021 — deterministic simulation testing at production scale, the method TigerBeetle later adopted. https://dl.acm.org/doi/10.1145/3448016.3457559
  6. TigerBeetle, Simulation Testing — DST applied to a financial database, including the deterministic scheduler and fault injection approach. https://tigerbeetle.com/blog/2023-07-06-simulation-testing-for-liveness/
  7. R. Haas, Who Contributed to PostgreSQL Development in 2025?, January 2026 — 266 principal authors, 66% of new lines by 26 people, 90% by 67; basis for the Postgres numbers above. http://rhaas.blogspot.com/2026/01/who-contributed-to-postgresql.html
  8. PostgreSQL Global Development Group, Core Team and contributor listings — 7-person core team, ~30 committers after ~30 years. https://www.postgresql.org/community/contributors/
  9. C. E. Jimenez et al., SWE-bench: Can Language Models Resolve Real-World GitHub Issues?, arXiv:2310.06770 — the benchmark that made iterative, test-feedback-driven agents the metric that matters. https://arxiv.org/abs/2310.06770
  10. J. Yang et al., SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering, arXiv:2405.15793 — evidence for harness > model: same model, better interface and feedback loop, dramatically better results. https://arxiv.org/abs/2405.15793
  11. M. Fowler, The Practical Test Pyramid, 2018 — the canonical reference for the pyramid shape and why each layer up costs an order of magnitude more. https://martinfowler.com/articles/practical-test-pyramid.html
  12. LDBC, Social Network Benchmark — an open, reproducible benchmark suite worth running against your actual workload before believing any database vendor's slides. https://ldbcouncil.org/benchmarks/snb/