The Microservices Regret
There is a pattern in enterprise engineering teams that has repeated enough times to be a reliable warning sign: a company adopts microservices because Netflix and Uber use them, spends 18 months building deployment infrastructure, distributed tracing, and inter-service communication, and then realizes that their actual problem was a poorly structured monolith that could have been fixed in three months without any of that overhead.
Microservices solve real problems. They also create real problems. The question is whether your specific situation has the problems that microservices solve, or the problems that microservices create.
The Distributed Systems Tax
When you split a monolith into services, every function call that used to happen in memory becomes a network call. Network calls fail. They are slow. They introduce partial failure modes that in-process code does not have.
A monolith's database transaction either commits or rolls back. A distributed operation across three services requires either distributed transactions (complex, slow, and prone to deadlock) or eventual consistency patterns (complex, subtle, and prone to edge-case bugs that only appear under specific failure conditions).
You also need: service discovery, distributed tracing, per-service deployments, per-service monitoring, inter-service authentication, and an understanding of which service owns which data. Each of these is a thing that needs to be built, maintained, and operated. None of them exist in a monolith.
The Modular Monolith: The Right Intermediate Step
Before splitting into services, most codebases benefit from a modular monolith: a single deployable unit where the internal structure enforces clean boundaries between components. Each module has its own data models, its own business logic, and its own interface. Modules communicate through defined interfaces, not direct database access or shared global state.
A well-structured modular monolith gives you most of the organizational benefits of microservices (teams can work independently, boundaries prevent coupling) without the operational overhead. If you later decide to extract a module into a service, the clean boundary makes the extraction straightforward. If you decide you do not need to, you have not paid the distributed systems tax for no reason.
When Splitting Actually Makes Sense
There are legitimate reasons to split a service out of a monolith. They are specific and usually business-driven, not architectural preferences:
- Independent scaling requirements. If your video encoding service needs 50 GPU instances during peak and 2 at night, but the rest of your application scales uniformly, extracting it to scale independently is justified.
- Independent deployment velocity. If two teams are blocked on each other's deployments because they share a release pipeline, separate deployment units solve a real organizational problem.
- Fundamentally different operational requirements. A real-time WebSocket service that needs to maintain persistent connections has different operational requirements than a CRUD API. Running them as separate services with different infrastructure is reasonable.
- Security isolation. A payments processing component that handles raw card data often needs to be isolated in a separate service to scope PCI compliance requirements.
Signs You Are Not Ready to Split
- You do not have automated deployments and monitoring in your monolith yet
- You are splitting because the monolith is hard to understand, not because it has a scaling or deployment problem
- You do not have the infrastructure team to manage multiple services in production
- The service boundaries you are drawing roughly match your current team structure (Conway's Law is not a technical architecture)
If your application has real scaling problems, real deployment bottlenecks, or real security isolation requirements, splitting services makes sense. If it has a mess of tangled code, the right solution is refactoring the monolith, not distributing the mess across the network. FriendsBit has worked on both sides of this decision, and if you are evaluating your architecture, we can help you think through whether splitting is the right call for your specific situation.