Somewhere around 2018, microservices became the default answer to every architecture question. I watched teams split perfectly functional monoliths into dozens of services because a conference talk told them to. The result? Three engineers spending their entire sprint debugging a distributed tracing issue that wouldn't have existed in the first place.
I've built both. For a healthcare platform connecting 700+ hospitals, microservices were the right call — ten services, each owned by a small team, each independently deployable. The pharmacy system could scale separately from the notification service, and a bug in appointment booking wouldn't take down prescription fulfillment. That's the sweet spot: genuinely different domains, different scaling needs, different deployment cadences.
But the hidden costs are real, and nobody talks about them at conferences. You need robust service discovery, distributed tracing, circuit breakers, and a team that understands eventual consistency. Debugging a request that touches five services is fundamentally harder than stepping through a monolith. Every network call is a potential failure point. Every service needs its own CI/CD pipeline, monitoring, and alerting. The operational overhead is massive.
Here's my framework: start with a well-structured monolith. Use clean module boundaries internally. When a specific module has genuinely different scaling requirements, or when your team has grown large enough that independent deployability matters more than simplicity — that's when you extract. Not before. The best architecture is the simplest one that solves your actual problems, not the one that looks impressive on a system design diagram.